Java笔记:内部类
1.把一个类放在另一类内部定义,这个定义在其他类里面的类就叫做内部类,包含内部类的类叫做外部类。内部类成员可以直接访问外部类的私有数据,但是外部类不能访问内部类的实现细节。
2.非静态内部类(没有static修饰的内部类)
package cn.lsl; public class Cow { private double weight; public Cow(){} public Cow(double weight){ this.weight = weight; } private class CowLeg{ private double length; private String color; public CowLeg(double length, String color) { super(); this.length = length; this.color = color; } public double getLength() { return length; } public void setLength(double length) { this.length = length; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public void info(){ System.out.println("颜色:" + color + ",高:" + length); System.out.println("重:" + weight); } } public void test(){ CowLeg cl = new CowLeg(2, "红色"); cl.info(); } public static void main(String[] args) { Cow cow = new Cow(123); cow.test(); } }
System.out.println("重:" + weight);这句代码在非静态内部类里可以直接访问外部类的private成员。
当调用非静态内部类的实例方法时,必须有一个静态内部类实例,而非静态内部类实例必须寄存在外部类实例里。
3.如果在外部类、内部类、内部类里方法里面出现了同名的变量,则可以通过this、外部类类名.this来访问
package cn.lsl; public class Variable { private String a = "外部类实例变量"; private class In{ private String a = "内部类实例变量"; public void info(){ String a = "局部变量"; System.out.println("外部类:" + Variable.this.a); System.out.println("内部类:" + this.a); System.out.println("局部变量:" + a); } } public void test(){ In in = new In(); in.info(); } public static void main(String[] args) { new Variable().test(); } }
三种访问形式:
System.out.println("外部类:" + Variable.this.a);
System.out.println("内部类:" + this.a);
System.out.println("局部变量:" + a);
4.外部类不能直接访问非静态内部类的实例Field,如果外部类需要访问非静态内部类的成员,则必须显示创建非静态内部类对象来调用访问其实例成员。
package cn.lsl; public class Outer { private int a = 5; class In{ private int b = 23; public void accessOuter(){ System.out.println("直接访问外部类的a的值" + a); } } public void accessIn(){ //System.out.println("内部类b的值:" + b); 编译出错,不能直接访问 System.out.println("内部类b的值:" + new In().b); } public static void main(String[] args) { Outer out = new Outer(); out.accessIn(); } }
非静态内部类对象必须寄存在外部类对象里面,而外部类对象不一定要有非静态内部类对象寄存其中。即,如果存在一个非静态内部类对象,一定要存在一个被它寄存的外部类对象。
5.静态内部类(使用static修饰的内部类)
(1)如果使用static修饰一个内部类,则这个内部类就属于外部类本身,不属于外部类的某个对象。
(2)静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。
package cn.lsl; public class StaticIn{ private int a = 5; private static int b = 23; static class In{ private static int age; public void accessOut(){ //System.out.println(a); //编译出错 System.out.println(b); } } }
(3)外部类依然不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员
package cn.lsl; public class StaticIn{ static class In{ private static int a = 5; private int b = 23; } public void accessIn(){ // System.out.println(a); //编译出错 System.out.println(In.a); // System.out.println(b); //编译出错 System.out.println(new In().b); } }
6.在外部类以外使用非静态内部类
如果希望在外部类以外的地方访问内部类,则内部类不能使用private访问控制权限,private修饰的内部类只能在外部类内部使用。
非静态内部类的对象必须寄存在外部类的对象里,因此创建非静态内部类之前,必须先创建其外部类对象。
eg: package cn.lsl; class Out { class In{ public In(String msg){ System.out.println(msg); } } } public class CreateIn{ public static void main(String[] args) { Out.In in = new Out().new In("测试信息"); } }
在外部类以外的地方创建非静态内部类实例必须使用外部类实例和new来调用非静态内部类的构造器。
7.在外部类以外使用静态内部类
因为静态内部类是外部类类相关的,因此创建内部类对象时无须创建外部类对象。
package cn.lsl; class StaticOut{ static class In{ public In(){ System.out.println("静态内部类的构造器"); } } } public class CreateStaticOut { public static void main(String[] args) { StaticOut.In in = new StaticOut.In(); } }
静态内部类和非静态内部类声明变量的语法是一样的,区别在于创建内部类对象时,静态内部类只需使用外部类即可调用构造器,而非静态内部类必须使用外部类对象来调用构造器。
8.局部内部类
如果把一个内部类放在方法里定义,则这个内部类就是一个局部内部类,局部内部类仅在该方法里有效。
由于局部内部类不能再方法以外的地方使用,因此局部内部类也不能使用访问控制符和static修饰符修饰。
如果需要用局部内部类定义变量,创建实例或派生子类,那么都只能在局部内部类所在的方法内进行。
package cn.lsl; public class LocalIn { public static void main(String[] args) { class InnerBase{ int a; } class InnerSub extends InnerBase{ int b; } InnerSub is = new InnerSub(); is.a = 5; is.b = 8; System.out.println(is.a + "," + is.b); } }
9.匿名内部类
(1)匿名内部类适合创建那种只需要一次使用的类,创建匿名内部类时会立即创建一个该类的实例,这个类定义即消失,匿名内部类不能重复使用。
(2)匿名内部类的格式
new 父类构造器(实参列表)|实现接口(){ }
(3)匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口。
(4)匿名内部类不能是抽象类。
(5)匿名内部类不能定义构造器,因为匿名内部类没有类名,所以无法定义构造器。但匿名内部类可以定义实例初始化块,通过实例初始化块来完成构造器需要完成的事情。
package cn.it.lsl; interface Product{ public double getPrice(); public String getName(); } public class AnonymousTest{ public void test(Product p){ System.out.println("购买" + p.getName() + ",花掉了" + p.getPrice()); } public static void main(String[] args) { AnonymousTest ta = new AnonymousTest(); ta.test(new Product(){ @Override public double getPrice() { // TODO Auto-generated method stub return 190; } @Override public String getName() { // TODO Auto-generated method stub return "声卡"; } }); } }
test()方法需要传入一个Product对象作为参数,但Product只是一个接口,因此此处考虑创建一个Product接口实现类的对象传入该方法。
上面匿名内部类部分也可以等价于下面这段代码
public class AnonymousTest implements Product{ @Override public double getPrice() { // TODO Auto-generated method stub return 100; } @Override public String getName() { // TODO Auto-generated method stub return "声卡"; } public void test(Product p){ System.out.println("购买" + p.getName() + ",花掉了" + p.getPrice()); } public static void main(String[] args) { AnonymousTest ta = new AnonymousTest(); ta.test(new AnonymousTest()); } }
10.如果通过继承父类来创建匿名内部类,匿名内部类将拥有和父类相似的构造器,此处的相似指的是拥有相同的形参列表。
package cn.it.lsl; abstract class Device{ private String name; public abstract double getPrice(); public Device(){} public Device(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class AnonymousInner { public void test(Device d){ System.out.println("购买一个" + d.getName() + ",花掉了" + d.getPrice()); } public static void main(String[] args) { AnonymousInner ai = new AnonymousInner(); ai.test(new Device("鼠标"){ @Override public double getPrice() { // TODO Auto-generated method stub return 23; } }); Device d = new Device() { { System.out.println("匿名内部类初始化块"); } @Override public double getPrice() { // TODO Auto-generated method stub return 32; } //重写父类的实例方法 public String getName(){ return "键盘"; } }; ai.test(d); } }
当创建匿名内部类时,必须实现接口或抽象父类里的所有抽象方法。如果有需要,也可以重写父类中的普通方法。
11.如果匿名内部类需要访问外部类的局部变量,则必须使用final修饰符来修饰外部类的局部变量
package cn.it.lsl; interface A{ void test(); } public class ATest { public static void main(String[] args) { //int age = 0; final int age = 0; A a = new A(){ @Override public void test() { System.out.println(age); } }; } }