一、static 关键字
1.1 静态域
如果把域定义成 static,每个类中只有一个这样的域,而每个对象 对于 所有的实例域 却都有自己的一份 拷贝 。比如: 需要给每个雇员赋予唯一的标识码:
class Employee
{
private static int nextID=1;
private int id;
}
现在,每个雇员都有自己的 id 域,但这个类的所有实例将共享一个 nextid 域。换言之,如果有 1000 个 Employee 类对象,则有 1000 个 实例域 id,但是只有一个静态域 nextId;即使没有一个 雇员对象,静态域 nextId 也存在,它属于类,不属于任何独立的对象。
1.2 静态常量
比如 Math.PI 、System.out 都是静态常量。
1.3 静态方法
静态方法是一种 不能向对象实施操作的方法,不能访问实例域,但是可以访问自身类的静态域。
可以使用对象访问静态方法。
使用静态方法的情景:
- 一个方法不需要访问对象状态,其所需参数都是通过显式参数提供。
- 一个方法只需要访问类的静态域。
- 做工厂方法。
❓静态内部类 与 普通内部类 的区别❓
静态内部类是不依赖于外部类的,也就说可以在不创建外部类对象的情况下创建内部类的对象。另外,静态内部类是不持有指向外部类对象的引用的,这个d读者可以自己尝试反编译class文件看一下就知道了,是没有 Outter this&0 引用的。
1.4 静态内部类
像线程池ThreadPoolExecutor中的四种拒绝机制CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy就是静态内部类。
与常规内部类不同,静态内部类可以有静态域 和 方法。
静态内部类 不依赖于外部类实例对象 而被实例化;不能访问外部类的普遍成员变量;只能访问外部类中的静态成员 和 静态方法。
二、 内部类
内部类是 定义在另一个类中的类。使用内部类的原因:
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
- 内部类可以对同一个包中的其他类隐藏起来。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较快捷。
内部类可以用 private 、protected、public 、(default)、abstract、final、static 修饰。
内部类中声明的所有 static 静态域必须是 final, 原因很简单,我们希望一个静态域 只有一个实例,不过对于每个外部对象,会分别有一个单独的内部类实例,如果这个域不是 final ,它就可能不是唯一的。
2.1 成员内部类
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。
为什么成员内部类可以访问外部类的成员呢?
编译时,会把 成员内部类 单独编译成一个字节码文件,编译器会自动为成员内部类添加一个指向外部类对象的引用。
2.2 局部内部类
可以在一个方法中定义局部类,例如:
public class OutterClass
{
public void start()
{
//在方法中定义局部类
class partClass implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the time,the time is"+new Date());
}
}
}
}
局部类不能用 public 或 private 访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。
局部类有一个优势,即 对外部世界可以完全隐藏起来,即使
OutterClass 类的代码也不能访问它。除了 start() 方法,其他方法不知道 局部类 的存在。而局部类可以访问它们的外部类,还可以访问局部变量。
2.3 匿名内部类
假设只创建这个类的一个对象,就不必命名了。这样的类称为 匿名内部类:
public class OutterClass
{
public void start()
{
//创建一个 实现 ActionListener 接口的类的新对象
// ActionListener 是个接口
partClass pc=new ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the time,the time is"+new Date());
}
}
}
}
这样 创建了 一个 实现 ActionListener 接口的类的新对象 pc
,需要实现的方法 actionPerformed 定义在括号里。
通常的语法格式:
new SuperType(construction parameters)
{
inner class
method and data
}
其实,SuperType 可以是接口,于是内部类就要实现 implements 这个接口,也可以是一个类,于是内部类就要扩展 extends 它。
由于构造器的名字必须与类名相同,而匿名类没有类名,所以,匿名类不能有构造器。取而代之的是,将构造器参数传递给 父类 的构造器,尤其在内部类实现接口的时候,不能有任何构造参数。
❓为什么局部内部类 和 匿名内部类 只能访问局部 final 变量❓
局部内部类 和 匿名内部类 都会被编译成 class 文件,像局部内部类,其变量的生命周期随着方法执行完毕就结束了,所以 Java 采用复制的方法:如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。使用的变量只是值和方法中 局部变量 的值相等,与 方法中的局部变量 是完全独立的。
如果在不同方法中 对 变量有修改,就会造成数据不一致,为了解决这个问题, 编译器就限定必须将 变量 限制为 final 类型,不允许对 变量 进行更改(对于引用类型的变量,是不允许指向新的对象),这样数据不一致性的问题就得以解决了。