1 Static
1.1 静态变量
静态变量也叫类变量,它属于一个类,而不是这个类的对象。从内存的角度来看,静态变量将会存储在 Java 虚拟机中一个名叫“Metaspace”(元空间,Java 8 之后)的特定池中。静态变量和成员变量有着很大的不同,成员变量的值属于某个对象,不同的对象之间,值是不共享的;但静态变量不是的,它可以用来统计对象的数量,因为它是共享的。
简单小结一下:
- 由于静态变量属于一个类,所以不要通过对象引用来访问,而应该直接通过类名来访问;
- 不需要初始化类就可以访问静态变量。
1.2 静态方法
静态方法也叫类方法,它和静态变量类似,属于一个类,而不是这个类的对象。简单总结:
- Java 中的静态方法在编译时解析,因为静态方法不能被重写(方法重写发生在运行时阶段,为了多态)。
- 抽象方法不能是静态的。
- 静态方法不能使用 this 和 super 关键字。
- 成员方法可以直接访问其他成员方法和成员变量。成员方法也可以直接方法静态方法和静态变量。
- 静态方法可以访问所有其他静态方法和静态变量。静态方法无法直接访问成员方法和成员变量。
1.3 静态代码块
- 一个类可以有多个静态代码块。
- 静态代码块的解析和执行顺序和它在类中的位置保持一致。
1.4 静态内部类
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
以上这段代码是不是特别熟悉,对,这就是创建单例的一种方式,第一次加载 Singleton 类时并不会初始化 instance,只有第一次调用 getInstance() 方法时 Java 虚拟机才开始加载 SingletonHolder 并初始化 instance,这样不仅能确保线程安全也能保证 Singleton 类的唯一性。不过,创建单例更优雅的一种方式是使用枚举。
简单小结一下:
- 静态内部类不能访问外部类的所有成员变量。
- 静态内部类可以访问外部类的所有静态变量,包括私有静态变量。
- 外部类不能声明为 static。
2 Transient
2.1 序列化
Java提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。
2.2 为何序列化
Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象 可以被写到数据库或文件中,也可用于 网络传输,一般当我们使用 缓存cache(内存空间不够有可能会本地存储到硬盘)或 远程调用rpc(网络传输)的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化。
为什么要不被序列化呢?其实主要是为了节省存储空间。优化程序!例如:
记得之前看
HashMap源码的时候,发现有个字段是用transient修饰的,我觉得还是有道理的,确实没必要对这个modCount字段进行序列化,因为没有意义,modCount主要用于判断HashMap是否被修改(像put、remove操作的时候,modCount都会自增),对于这种变量,一开始可以为任何值,0当然也是可以(new出来、反序列化出来、或者克隆clone出来的时候都是为0的),没必要持久化其值。
2.3 小结
1、变量被transient修饰,变量将不会被序列化
2、transient关键字只能修饰变量,而不能修饰方法和类。
3、被static关键字修饰的变量不参与序列化,一个静态static变量不管是否被transient修饰,均不能被序列化。
4、final变量值参与序列化,final transient同时修饰变量,final不会影响transient,一样不会参与序列化
5.本地变量是不能被transient关键字修饰的。
3 This
3.1 属性
This 代表对当前对象的一个引用
所谓当前对象,指的是调用当前类中方法或属性的那个对象
this只能在方法内部使用,表示对“调用方法的那个对象”的引用
this.属性名,表示本对象自己的属性
当对象的属性和方法或构造器中,名称相同时,用 this 以示区分。
public class Person{
String name;
public Person(String name){
//用以区分对象中的属性名,参数名
this.name = name;
}
}
3.2 方法
3.2.1 构造器
this关键字调用类的重载构造函数,this关键字必须位于构造函数的第一行
public class Person{
String name;
int age;
public Person(int age){
this.age = age;
}
public Person(String name){
this(1);
this.name = name;
}
}
3.2.2普通方法
this.方法名:表示当前对象自己的方法
public class Student{
public void eat(){
System.out.println("吃饭");
}
public void talk(){
this.eat();
System.out.println("吃完饭再说话");
}
}
3.3 注意点
this不能用于静态方法和静态块
4 Super
4.1 属性
在子类的方法或构造器中,通过使用 super.属性 来访问父类属性
应用场景:
当子类和父类中定义了同名的属性时,想要调用父类中声明的属性,需要通过 super.属性 的方式来表明调用的是父类中声明的属性。
public class Car{
//车架号
int id;
}
class Benz extends Car{
//内部车辆编号
int id;
public void showInfo(){
System.out.println("车架号是:" + super.id + " 内部车辆编号:" + id);
}
}
4.2 方法
4.2.1 构造器
在继承中,子类的构造函数必须依赖父类提供的构造函数,super(参数列表) 访问父类的构造函数,super 调用父类的构造函数,必须位于子类构造函数中的第一行。
//父类
public class Person{
public Person(){}
}
//子类
public class Student extends Person{
public Student(){
super();
}
}
//父类中只有带参构造方法
public class Base {
String name;
int age;
public Base(String name, int age){
}
}
//子类构造方法必须依赖父类
public class Son extends Base{
public Son(){
//如果父类提供的只有带参构造方法,子类的构造方法必须依赖父类提供的现有构造方法;
//使用super(参数列表)去访问父类提供的构造函数,且必须明确写出参数;
//super必须在第一行
super("张三", 20);
}
}
4.2.2 普通方法
在子类的方法或构造器中,通过使用 super.方法名 来调用父类的方法
public class Car{
public void drive(){
System.out.println("驾驶");
}
}
class Benz extends Car{
public Benz(){
//访问父类方法
super.drive();
}
}
4.3 注意
- super() 是调用父类的构造函数,必须在构造函数的第一行;
- this() 是调用本类的构造函数,必须在构造函数的第一行;
- super() 和 this() 在同一个构造方法中,不能同时出现。