Java基础-关键字Part2

124 阅读6分钟

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提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。

image.png

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 注意

image.png
  • super() 是调用父类的构造函数,必须在构造函数的第一行;
  • this() 是调用本类的构造函数,必须在构造函数的第一行;
  • super() 和 this() 在同一个构造方法中,不能同时出现