一、内部类介绍
一、内部类介绍
一个类定义在另一个类的内部,前者称之为内部类,后者称之为外部类。
内部类一般用在定义它的类和语句块内,在外部使用它时需要给出完整的名称,且不可以与外部类的名称相同。
使用场景为当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内 部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使 用内部类。比如:集合中的某些结构
二、内部类特点
内部类是一个编译器行为,与虚拟机无关。编译器会将内部类转换为常规的类文件,用$分隔外部类类名与内部类名,而虚拟机对此一无所知。
内部类可以对同一个包下的其他类隐藏(只有内部类可以是private修饰,而常规类可以有包可见性(缺省)与公共可见性(public))。
内部类可以访问定义所在的这个类的作用域中的数据,包括原有的私有数据。
三、内部类分类
静态内部类
非静态内部类
局部内部类
匿名内部类
二、静态内部类
一、特点:
Inner class 可以声明为private 和protected、缺省、public,final(最终类),abstract(可被继承)。
Inner class 可以定义成员变量,构造方法,方法(可以是static静态的)
Inner class 并不 包含一个隐式引用,指向实例化这个对象的外部类对象。
Inner class 可以访问out class 的属性,方法(包括私有的)。
Out calss 的静态成员部分使用内部类时,可以考虑内部类声明为静态的。
二、注意点:
只要内部类不需要访问外围类对象,就应该使用静态内部类。
在域之外,引用内部类:OutClass.InnerClass
ArrayAlg.Pair pair = new ArrayAlg.Pair(1.1, 1.2);
三、示例:
public class ArrayAlg {
public static class Pair{
private double first;
private double second;
public Pair(double first, double second) {
this.first = first;
this.second = second;
}
public double getFirst() {
return first;
}
public void setFirst(double first) {
this.first = first;
}
public double getSecond() {
return second;
}
public void setSecond(double second) {
this.second = second;
}
}
public static Pair minmax(double[] values){
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for(double v : values){
if(min > v) min = v;
if(max < v) max = v;
}
return new Pair(min,max);
}
}
public static void main(String[] args) {
ArrayAlg.Pair minmax = ArrayAlg.minmax(new double[]{1.1, 4.5, 6.7, 8.9});
System.out.println("min = " + minmax.getFirst() + " max = " + minmax.getSecond());
}
}
三、非静态内部类
一、特点:
Inner class 可以声明为private 和protected、protected、缺省,final(最终类),abstract(可被继承)。
Inner class 可以定义成员变量,构造方法,方法,但不可以定义为static的成员。
Inner class 包含一个隐式引用,指向实例化这个对象的外部类对象。
Inner class 可以访问out class 的属性,方法(包括私有的)。
二、注意点:
关于隐式引用:
这个引用在内部类的定义中是不可见的。
外部类的引用在构造器中设置。构造器会修改所有的内部类构造器,添加一个外部类引用的参数。
在域之外,引用内部类:OutClass.InnerClass
三、示例
//语音时钟类
public class TalkingClock {
private int interval;
private boolean beep;
public TalkingClock(int interval, boolean beep) {
this.interval = interval;
this.beep = beep;
}
//开始时钟
public void start(){
TimePrinter listener = new TimePrinter();
Timer timer = new Timer(interval, listener);
timer.start();
}
public class TimePrinter implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("现在时间是:" + Instant.ofEpochSecond(e.getWhen()));
if(beep) Toolkit.getDefaultToolkit().beep();
}
}
public static void main(String[] args) {
TalkingClock clock = new TalkingClock(1000, true);
clock.start();
//一直让程序运行,直到你确定结束
JOptionPane.showMessageDialog(null,"Quit");
System.exit(0);
}
}
四、局部内部类
一、特点:
声明局部内部类不能有访问说明符(public 或private )。局部内部类的作用域被限定在声明这个局部类的块中。
只能够定义在方法和代码块内部,而且是先声明后使用。
但它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型。
局部内部类可以使用外部方法的局部变量,但是必须是事实的最终变量,即一旦被赋值就不可改变。由于局部内部类和局部变量的声明周期不同所致。
二、示例
public class TalkingClock1 {
//开始时钟
public void start(int interval, boolean beep){
//局部内部类
class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("现在时间是:" + Instant.ofEpochSecond(e.getWhen()));
if(beep) Toolkit.getDefaultToolkit().beep();
}
}
TimePrinter listener = new TimePrinter();
Timer timer = new Timer(interval, listener);
timer.start();
}
}
关于局部变量在局部内部类中使用的说明:
编译器检测到局部变量的访问,为每一个变量创建相应的实例字段,并将局部变量复制到构造器,从而初始化这些实例字段。
class TalkingClock1$1TimePrinter{
TalkingClock1$1TimePrinter(TalkingClock1,boolean);
public void actionPerformed(java.awt.event.ActionEvent);
final boolean val$beep;
final TalkingClock1 this$0;
}
此处的beep在局部内部类中被使用,因此会为其创建一个实例字段,并通过构造器初始化。
六、匿名内部类
一、概念
使用局部内部类,如果只想创建这个类的一个对象,设置不需要为类指定名字,这样的一个类称之为匿名内部类。
一个匿名内部类一定是在new的后面,用其隐含实现一个接口或 实现一个类。
new SuperType(参数列表){
//内部类方法与数据
}
如果superType是一个类,那么内部类就要扩展这个类。
如果superType是一个接口,那么内部类就要实现这个接口。
同时由于构造器的名称必须与类名相同,而匿名内部类没有类名,因此匿名内部类没有构造器。
实际上构造参数是要传递给超类构造器,因此内部类实现一个接口,就不能有任何参数(接口无构造器方法),但仍然提供一对小括号。
new InterfaceType(){
//方法与数据
}
尽管匿名类不能有构造器,但可以提供一个对象初始化块。
new SuperType(参数列表){
{
}
//内部类方法与数据
}
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一 个实例。
二、示例
public class Test{
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("hello");
list1.add("word");
print(list1);
//或
//双括号初始化,外层括号建立了ArrayList的一个匿名子类,内层括号则是一个对象初始化块
print(new ArrayList<String>(){
{
add("hello");
add("word");
}
});
}
public static void print(ArrayList<String> lists){
for(String list:lists){
System.out.println(list);
}
}
}
注意:建立一个匿名子类通常会很方便,但对于以下equasl方法使用匿名子类可能会失败
....
if(getClass() != other.getClass()) return false;
...
public class Test {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o2,o1);
}
});
set.add(1);
set.add(2);
set.add(3);
System.out.println(set);
}
}