- 类只加载一次
静态变量能否定义在构造方法中? 不能
注意:静态变量内的赋值是所有对象包括子类共享的是在方法区内的,而成员变量的赋值是是在堆内存是伴随着对象的,其他对象是不共享的。
方法区加载类信息,在加载Person类信息的时候一起加载静态变量kongfu并附上了默认值。在栈内存中执行main方法先声明一个Person类的对象并创建一个地址值为0x123的对象,给成员变量附上默认值,用静态变量的地址找到静态变量的位置并附上值。要是此时在创建一个新的对象时将会在堆内存中开辟出一个新的区域给它,但是静态变量时在创建对象之前就存在的,静态变量的值只能被覆盖。
静态方法
static修饰的方法称之为静态方法。静态方法在类加载的时候加载到方法区。并没有执行只是存储在方法区,在方法被调用的时候到栈内存中执行。静态方法本身也是先于对象存在,所以习惯上是通过类名来调用静态方法。
静态方法中能否使用this/super? 不可以
静态方法中能否调用非静态方法?不可以
非静态方法中能否调用静态方法? 可以
静态方法能否被继承? 可以
静态方法能否被重写? 不可以
父子类中存在方法签名相同的静态方法,称之为隐藏(hide),隐藏也适用于重写的五个原则
静态代码块
static修饰的{}就是静态代码块,会在类加载的时候执行.静态代码块只会执行一次。
一般用静态代码块读取文件信息。
代码块执行顺序问题:1静态代码块 2构造代码块 3构造方法
static面试题
java文件经过编译 成为class文件,class文件加载到了内存中。分为:
- 加载:将class文件转换为二进制的字节码
- 校验:检查字节码的安全性
- 准备:将静态变量存放到方法区并且分配空间,然后标记一个默认值,标记的值有可能被舍弃。
- 解析:常量符号引用替换
- 初始化:执行静态变量的赋值以及静态代码块,按照代码的顺序执行。
// 准备:加载sd标记值为null,i标记值为0,j标记值为0
// 初始化:先执行sd = new SDemo();执行构造方法,会将i的标记值变为1,j的标记值变为1.然后继续执行i = 5,i舍弃标记值1,给5赋值给i。
// 继续给j赋值,发现j没有初始化值,将标记值1作为初始化赋值给j。
class SDemo{
static SDemo sd = new SDemo();
static int i = 5;
static int j;
public SDemo(){
i++;
j++;
}
}
// 5 和 1
// // 准备:i标记值为0,j标记值为0
// 初始化:执行静态代码块,将i的标记值变为1,j的标记值变为1.然后继续执行i = 5,i舍弃标记值1,给5赋值给i。
// 继续给j赋值,发现j没有初始化值,将标记值1作为初始化赋值给j。
class SE{
static {
SE.i++;
SE.j++;
}
static int i = 5;
static int j;
}
final
final修饰的是基本数据类型,其值不能改变,final修饰引用数据类型,地址不能发生改变,其中的元素或者属性可以改变。
final修饰成员变量,要求在对象创建完成之前赋值
final修饰静态变量,要求在类加载完成之前赋值。
final修饰的方法称之为最终方法,不能被重写(隐藏)
final修饰的方法可以被继承。
final修饰的方法可以重载。
final修饰的类称之为最终类,不能被继承。
abstract
- abstract可以修饰方法和类,修饰的方法是抽象方法,不需要方法体。修饰的类称之为抽象类,抽象方法只能存在抽象类和接口中。
- 抽象方法可以被继承,子类要重写抽象方法.或者子类也成为抽象类
- 抽象类不能实例化,不能创建对象
- 抽象类还是一个类,可以放抽象方法
- 抽象类中可以编写普通方法和静态方法
- 抽象类中可以有构造方法
- 抽象类因为要被继承,所以一定不是最终类
- 抽象方法要被重写,所以不能被private/final/static修饰
- 抽象类也是一个类,受到java单继承的限制。
接口
jdk1.8之前的接口特性
接口就是功能的集合。在jdk1.8之前,接口中的方法只能是抽象方法,jdk1.8开始接口中允许更多的方法。
接口不是类,但是接口依然会生成.class文件。
接口不能实例化,需要类实现接口中的抽象方法,通过向上转型使用接口
类和接口之间用implements关键字来产生联系
接口中的抽象方法必须被public abstract 修饰,可以省略,系统默认提供public abstract 。
接口不能实例化,也没有构造方法。接口在编译完成之后会产生class文件,但是接口不是类。
一个类可以实现多个接口
一个接口可以继承多个接口
关于接口的强行转换:在Java编译时时不考虑接口的继承关系的,所以不管是否存在继承关系Java都会编译通过,也就是说任何一个接口都可以进行强行转换。
SA a = new B1();
// 在编译期间,a对象的声明类型是SA类型
// a对象要强转的类型是B1类型,B1和SA有继承关系,所以编译通过
// 在运行期间,发现a的实际类型就是B1,类型匹配,可以转换
B1 b = (B1)a;
接口中的实体方法
在jdk1.8之后允许有实体方法,需要使用default或者static修饰
@FunctionalInterface // 函数式接口 保证接口中只有一个抽象方法
接口当中的变量都会被 public static final所修饰,为静态常量。
jdk8 特性 默认方法,静态方法
jdk9可以定义私有的实体方法,私有静态方法
内部类
在类中的类就是内部类,内部类也是类,也可以通过编译生成class文件
内部类分为:
- 局部/方法内部类
- 成员内部类
- 静态内部类
- 匿名内部类
局部内部类
- 定义在方法中的内部类
- 可以使用外部类中的一切属性和方法
- 可以定义非静态变量和非静态方法
- 不可以定义静态变量和静态方法
- 可以定义静态常量
成员内部类
在类中方法外的内部类
- 可以使用外部类中所有的属性和方法
- 可以定义非静态属性和非静态方法
- 不可以定义静态属性和静态方法
- 可以定义静态常量
- 成员内部类可以使用一切修饰类的修饰符:比如public private abstract
静态内部类
被static修饰的类称之为内部类
- 不能使用外部类的非静态属性和非静态方法
- 可以使用外部类的静态属性和静态方法
- 可以定义一切的属性和方法
匿名内部类
没有名字的内部类,代表类的子类或者接口的实现类。
只要能被继承的类都可以使用匿名内部类。
最终类不能使用匿名内部类
匿名内部类的定义格式:接口名称 对象名 = new 接口名称() {//覆盖重所有抽象方法};