大家好呀,这期我就继续在代码中带大家回顾Java中的基础知识,可以自己敲一下看看,加深理解。根据这些知识我还整理了一张图,基本上每个知识点都有注释详解
点这里加群领取资料
静态字段和方法
静态字段:static 字段
在一个class中定义的字段,我们称之为实例字段。实例字段的特点是,每个实例都有独立的字段,各个实例的同名字段互不影响。还有一种字段,是用static修饰的字段,称为静态字段:static field。实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段。举个例子:
class Person {
public String name;
public int age;
public static int number;
}
我们来看看下面的代码:
public class Main {
public static void main(String[] args) {
Person ming = new Person("Xiao Ming", 12);
Person hong = new Person("Xiao Hong", 15);
ming.number = 88;
System.out.println(hong.number);
hong.number = 99;
System.out.println(ming.number);
}
}
class Person {
public String name;
public int age;
public static int number;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
} 运行结果:
88 99
对于静态字段,无论修改哪个实例的静态字段,效果都是一样的:所有实例的静态字段都被修改了,原因是静态字段并不属于实例。虽然实例可以访问静态字段,但是它们指向的其实都是Person class的静态字段。所以,所有实例共享一个静态字段。因此,不推荐用实例变量.静态字段去访问静态字段,因为在Java程序中,实例对象并没有静态字段。在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转换为类名.静态字段来访问静态对象。 推荐用类名来访问静态字段。可以把静态字段理解为描述class本身的字段(非实例字段)。对于上面的代码,更好的写法是:
Person.number = 99;
System.out.println(Person.number);
接口的静态字段
因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型:
public interface Person {
public static final int MALE = 1;
public static final int FEMALE = 2;
}
实际上,因为interface的字段只能是public static final类型,所以我们可以把这些修饰符都去掉,上述代码可以简写为:
public interface Person {
int MALE = 1;
int FEMALE = 2;
}
编译器会自动把该字段变为public static final类型。
静态方法:static 方法
静态字段,就有静态方法。用static修饰的方法称为静态方法。调用实例方法必须通过一个实例变量,而调用静态方法则不需要实例变量,通过类名就可以调用。静态方法类似其它编程语言的函数。例如:
public class Main {
public static void main(String[] args) {
Person.setNumber(99);
System.out.println(Person.number);
}
}
class Person {
public static int number;
public static void setNumber(int value) {
number = value;
}
}
运行结果:
99
因为静态方法属于class而不属于实例,因此,静态方法内部,无法访问this变量,也无法访问实例字段,它只能访问静态字段。通过实例变量也可以调用静态方法,但这只是编译器自动帮我们把实例改写成类名而已。通常情况下,通过实例变量访问静态字段和静态方法,会得到一个编译警告。 静态方法经常用于工具类。例如: Arrays.sort() Math.random() 静态方法也经常用于辅助方法。注意到Java程序的入口main()也是静态方法。
静态变量、静态方法初始化顺序
静态代码在类的初始化阶段被初始化。
非静态代码则在类的使用阶段(也就是实例化一个类的时候)才会被初始化。
静态变量 可以将静态变量理解为类变量(与对象无关),而实例变量则属于一个特定的对象。 静态变量有两种情况:
静态变量是基本数据类型,这种情况下在类的外部不必创建该类的实例就可以直接使用 静态变量是一个引用。这种情况比较特殊,主要问题是由于静态变量是一个对象的引用,那么必须初始化这个对象之后才能将引用指向它。 因此如果要把一个引用定义成static的,就必须在定义的时候就对其对象进行初始化。
public class TestForStaticObject{
static testObject o = new testObject ();
public static void main(String args[]){
}
}
包
包的定义
Java定义了一种名字空间,称之为包:package。一个类总是属于某个包,类名(比如Person)只是一个简写,真正的完整类名是包名.类名。使用package来解决名字冲突. 在前面的代码中,我们把类和接口命名为Person、Student、Hello等简单名字。 在现实中,如果小明写了一个Person类,小红也写了一个Person类,现在,小白既想用小明的Person,也想用小红的Person,怎么办?如果小军写了一个Arrays类,恰好JDK也自带了一个Arrays类,如何解决类名冲突? 在Java中,我们使用package来解决名字冲突。 Java定义了一种名字空间,称之为包:package。一个类总是属于某个包,类名(比如Person)只是一个简写,真正的完整类名是包名.类名。 例如: 小明的Person类存放在包ming下面,因此,完整类名是ming.Person; 小红的Person类存放在包hong下面,因此,完整类名是hong.Person; 小军的Arrays类存放在包mr.jun下面,因此,完整类名是mr.jun.Arrays; JDK的Arrays类存放在包java.util下面,因此,完整类名是java.util.Arrays。 在定义class的时候,我们需要在第一行声明这个class属于哪个包。 小明的Person.java文件:
package ming;
public class Person {
}
小军的Arrays.java文件:
package mr.jun;
public class Arrays {
}
在Java虚拟机执行的时候,JVM只看完整类名,因此,只要包名不同,类就不同。包可以是多层结构,用.隔开。例如:java.util。 要特别注意:包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。 没有定义包名的class,它使用的是默认包,非常容易引起名字冲突,因此,不推荐不写包名的做法。
包作用域
位于同一个包的类,可以访问包作用域的字段和方法。不用public、protected、private修饰的字段和方法就是包作用域。 位于同一个包的类,可以访问包作用域的字段和方法。不用public、protected、private修饰的字段和方法就是包作用域。例如,Person类定义在hello包下面:
package hello;
public class Person {
void hello() {
System.out.println("Hello!");
}
}
Main类也定义在hello包下面:
package hello;
public class Main {
public static void main(String[] args) {
Person p = new Person();
p.hello();
}
}
import关键字
import的目的是为了让你写Xyz不用写abc.def.Xyz,和访问权限是两回事 java编译器最终编译出的.class文件只使用完整类名,因此,在代码中,当编译器遇到一个class名称时: 如果是完整类名,就直接根据完整类名查找这个class; 如果是简单类名,按下面的顺序依次查找: 查找当前package是否存在这个class; 查找import的包是否包含这个class; 查找java.lang包是否包含这个class。 如果按照上面的规则还无法确定类名,则编译报错。 我们来看一个例子:
package test;
import java.text.Format;
public class Main {
public static void main(String[] args) {
java.util.List list;
Format format = null;
String s = "hi";
System.out.println(s);
MessageFormat mf = null;
}
}
因此,编写class的时候,编译器会自动帮我们做两个import动作: 默认自动import当前package的其他class; 默认自动import java.lang.*。 注意:自动导入的是java.lang包,但类似java.lang.reflect这些包仍需要手动导入。 如果有两个class名称相同,例如,mr.jun.Arrays和java.util.Arrays,那么只能import其中一个,另一个必须写完整类名。
作用域
作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。 作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突。 public、protected、private这些修饰符可以用来限定访问作用域。
局部变量
在方法内部定义的变量称为局部变量,局部变量作用域从变量声明处开始到对应的块结束。方法参数也是局部变量。
package abc;
public class Hello {
void hi(String name) {
String s = name.toLowerCase();
int len = s.length();
if (len < 10) {
int p = 10 - len;
for (int i=0; i<10; i++) {
System.out.println();
}
}
}
}
我们观察上面的hi()方法代码: 方法参数name是局部变量,它的作用域是整个方法,即①~⑩; 变量s的作用域是定义处到方法结束,即②~⑩; 变量len的作用域是定义处到方法结束,即③~⑩; 变量p的作用域是定义处到if块结束,即⑤~⑨; 变量i的作用域是for循环,即⑥~⑧。 使用局部变量时,应该尽可能把局部变量的作用域缩小,尽可能延后声明局部变量。
final修饰符
Java还提供了一个final修饰符。final与访问权限不冲突,它有很多作用。
- 用final修饰class可以阻止被继承
- 用final修饰method可以阻止被子类覆写
- 用final修饰field可以阻止被重新赋值
- 用final修饰局部变量可以阻止被重新赋值
classpath和jar
classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class。 因为Java是编译型语言,源码文件是.java,而编译后的.class文件才是真正可以被JVM执行的字节码。因此,JVM需要知道,如果要加载一个abc.xyz.Hello的类,应该去哪搜索对应的Hello.class文件。 所以,classpath就是一组目录的集合,它设置的搜索路径与操作系统相关。例如,在Windows系统上,用;分隔,带空格的目录用""括起来,可能长这样: C:\work\project1\bin;C:\shared;“D:\My Documents\project1\bin” 在Linux系统上,用:分隔,可能长这样: /usr/shared:/usr/local/bin:/home/liaoxuefeng/bin 现在我们假设classpath是.;C:\work\project1\bin;C:\shared,当JVM在加载abc.xyz.Hello这个类时,会依次查找: <当前目录>\abc\xyz\Hello.class C:\work\project1\bin\abc\xyz\Hello.class C:\shared\abc\xyz\Hello.class 注意到.代表当前目录。如果JVM在某个路径下找到了对应的class文件,就不再往后继续搜索。如果所有路径下都没有找到,就报错。 classpath的设定方法有两种: 在系统环境变量中设置classpath环境变量,不推荐; 在启动JVM时设置classpath变量,推荐。 我们强烈不推荐在系统环境变量中设置classpath,那样会污染整个系统环境。在启动JVM时设置classpath才是推荐的做法。实际上就是给java命令传入-classpath或-cp参数: java -classpath .;C:\work\project1\bin;C:\shared abc.xyz.Hello 或者使用-cp的简写: java -cp .;C:\work\project1\bin;C:\shared abc.xyz.Hello 没有设置系统环境变量,也没有传入-cp参数,那么JVM默认的classpath为.,即当前目录: java abc.xyz.Hello 上述命令告诉JVM只在当前目录搜索Hello.class。 在IDE中运行Java程序,IDE自动传入的-cp参数是当前工程的bin目录和引入的jar包。 不要把任何Java核心库添加到classpath中!JVM根本不依赖classpath加载核心库! 更好的做法是,不要设置classpath!默认的当前目录.对于绝大多数情况都够用了。
模块
Java9中引入了模块化的概念,整个内容有点繁杂,想要深入研究的同学可以参考《Java9模块化开发核心原则与实践》一书,没有的同学可以进最上方交流群获取.
最后,祝大家早日学有所成,拿到满意offer,快速升职加薪,走上人生巅峰。 可以的话请给我一个三连支持一下我哟,我们下期再见
|