12.1内部类
(笔记:当一个类只需要在一个类中使用而与其他类无关时,就可以将这个类写在使用这个类的内部,这就是内部类的使用场合)
内部类是指在一个外部类的内部再定义一个类。类名不需要和文件名相同。
内部类可以是静态static的,也可用public,default,protected和private修饰。(而外部顶级类即类名和文件名相同的只能使用public和default)。
注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。编译完成后出现Outer.class和Outer$Inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。
12.1.1成员内部类
1.成员内部类,就是作为外部类的成员,可以直接使用外部类的所有成员属性和方法,即使是private的。同时外部类要访问内部类的所有成员变量/方法,则需要通过内部类的对象来获取。
2.要注意的是,成员内部类不能含有static的变量和方法。因为成员内部类需要先创建了外部类对象,才能创建它自己的。
- 在成员内部类要引用外部类对象时,使用Outer.this来表示外部类对象;
(当内部类与外部类有相同的属性/方法的时候,要特指使用外部类的属性和方法时:外部类名.this.属性名/方法名)
4.而需要创建内部类对象时,可以使用:
Outer.Inner inner = outerobj.new Inner();(外部类名.内部类名 对象引用名= 外部类对象引用名.new 内部名())
一般只在外部类中使用,不在外部使用内部类
public class Outer {
/**
*只有Outer的对象才有Inner内部类存在,即成员内部类是属于外部类的对象的
*/
public class Inner {//成员内部类
public void print(String str) {
System.out.println(str);
}
}
}
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); //创建成员内部类对象
inner.print("Outer.new");
}
12.1.2静态内部类(嵌套内部类)
1.静态内部类(嵌套内部类),就是修饰为static的内部类。声明为static的内部类,不需要内部类对象和外部类对象之间的联系,就是说我们可以直接引用Outer.Inner,即不需要创建外部类对象,也不需要创建内部类。
2.静态内部类和普通的内部类还有一个区别:普通内部类不能有static数据和static属性,也不能包含嵌套类(静态类),但静态内部类可以。而静态内部类不建议声明为private,一般声明为public,方便调用。
3.静态内部类的内部 不能访问外部类的非静态成员
12.1.3 匿名内部类
有时候我们为了免去给内部类命名,便倾向于使用匿名内部类,因为它没有名字。
匿名内部类可以定义在方法中和外部类中当作成员变量的值,此时成员变量为匿名内部类父类的引用,可以为静态成员
匿名内部类是不能加访问修饰符的。要注意的是,new 匿名类,这个类是要先定义的,看下面例子:
public class Main{
public static void main(String [] args){
final int i = 10;
//匿名内部类的匿名对象(因为匿名内部类是通过new实现的,所以整体是一个对象(匿名类对象))
new Random(){
int a;
public void test(){
//在匿名内部类中使用局部变量,必须是final修饰的(jdk1.8可以省略final不会报错)
System.out.print(i);//不能修改局部变量i
}
};
}
}
public class Outer {
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
}
public Inner getInner(final String name, String city) {
return new Inner() {
private String nameStr = name;
public String getName() {
return nameStr;
}
};
}
}
//注释后,编译时提示类Inner找不到,即匿名内部类的父类一定要先存在
/* interface Inner {
String getName();
} */
注意:在这个例子,留意外部类的方法的形参,当所在的方法的形参需要被内部类里面使用时,该形参必须为final。因为,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
//接口与匿名内部类结合使用的实例:模拟办公室通过监控器来监控工地工人搬砖的情况,思路: 1.建一个Office类 2.建一个工地类 3.肯定要用到监控,但监控器可以来自不同的厂家,所以要定义监控器的功能,厂家生产的监控器只要满足工地监控器的功能即可使用, 所以定义一个OncameraListenner接口,即监控器的标准 4.因为要监控工地,所以要在工地装好监控器,即在工地类中定义一个监控器listenner成员,去调用监控器的各种功能, 同时给工地监控一个设置使用哪家监控器的方法setListenner(OncameraListenner listenner) 5.最后,在办公室要打开监控时需要指定到底使用哪家厂商的监控器,调用setListenner()方法时指定使用哪家厂商的监控器, 即传入OncameraListenner接口的实现类对象,这里为了展现匿名类的应用,所以使用了匿名内部类来实现接口。 /** * 工厂搬砖与办公室信息传输协议 * @author Administrator * */ public interface OncameraListenner { /** * 可以通知办公室开始搬砖的信息 */ void start(); /** * 可以通知办公室搬砖过程的信息 */ void progress(String name, int progress, String from, String to); /** * 可以通知办公室结束搬砖的信息 */ void stop(); } /** * 工地 * @author Administrator * */ public class ConstructionSite { private OncameraListenner listenner; public void setListenner(OncameraListenner listenner){ this.listenner=listenner; } public void banzhuan(String name,int sum){ /** * 开始搬砖 */ if(listenner!=null){ listenner.start(); } //模拟耗时操作 for (int i = 1; i <= sum; i++) { /** * 搬砖的进度 */ if(listenner!=null){ listenner.progress(name, i, "澳大利亚", "深圳"); } } /** * 搬砖结束 */ if(listenner!=null){ listenner.stop(); } } } /** * 办公室 * @author Administrator * */ public class Office { /** * 希望在这里打印搬砖的信息 * @param args */ public static void main(String[] args) { ConstructionSite cs = new ConstructionSite(); //给工厂装监控器(接口回调) cs.setListenner(new OncameraListenner() { @Override public void stop() { System.out.println("结束搬砖啦"); } @Override public void start() { System.out.println("开始搬砖啦"); } @Override public void progress(String name, int progress, String from, String to) { System.out.println(name+"从"+from+"搬砖到"+to+",进度为:"+progress); } }); //指挥张三开始搬砖 cs.banzhuan("张三", 100); } }