1 内部类的含义
内部类:
把类定义在其他类的内部,这个类就被称为内部类。(类可以嵌套定义,方法不可以嵌套定义)
编译成字节码文件:Outer.class(外部类), Outer$Inner.class(内部类)
为什么需要内部类?
外部类 内部类
PC -- CPU
a. 保护CPU
b. 方便PC和CPU的交互
人 心脏
a. 保护心脏
b. 方便心脏和其他器官的交互
a. 保护内部类
b. 方便内部类和外部类的交互
访问特点(重点):
内部类访问外部类:直接访问, 可以访问外部类私有的成员
外部类访问内部类:创建对象,可有访问内部类私有的成员
分类:
成员位置:成员内部类, 静态内部类
局部位置:局部内部类, 匿名内部类(掌握)
public class Outer {
class Inner {
}
public static void main(String[] args) {
}
}
2 成员内部类
成员内部类:(外部类对象所有)
a.内部类访问外部类成员:
直接访问(好像定义在自己的类中一样),并且可以访问外部类的私有成员。
如果内部类成员隐藏了外部类成员,那么该如何访问外部类成员?
使用外部类名.this。Caution!!!
b.外部类访问内部类成员:
创建内部类对象,通过该对象访问内部类成员,并且可以访问内部类的私有成员。
c.其它类访问内部类成员:
创建内部类对象来访问,并且受访问权限修饰符限制。
此时创建内部类对象的格式:外部类名.内部类名 对象名 = 外部类对象.new 内部类名();
注意:一般情况下,我们会将内部类用private修饰,以保护起来。
为什么内部类可以直接访问外部类成员?为什么创建内部类对象,需要一个外部类对象?
对Outer1$Inner1.class(内部类)文件反编译之后:
final Outer1 this$0; // 外部类类型
// 构造方法
Outer1$Inner1() {
this$0 = Outer1.this; // 外部类的对象赋值给上面的成员变量this$0
super();
}
发现内部类秘密捕获了外部类对象,Outer1.this就代表创建内部类对象的那个外部类对象。
public class Outer1 {
private int a = 10;
public int b = 20;
class Inner1 {
int a = 100;
int b = 200;
private int num1 = 1000;
private int num2= 2000;
public void show() {
System.out.println(a);
System.out.println(b);
System.out.println(Outer1.this.a);
System.out.println(Outer1.this.b);
}
}
public void method() {
Inner1 inner = new Inner1();
System.out.println(inner.num1);
System.out.println(inner.num2);
}
public static void main(String[] args) {
Outer1 outer = new Outer1();
outer.method();
Inner1 inner = outer.new Inner1();
inner.show();
}
}
其他类访问成员内部类成员:创建对象,受访问权限限制
public class Demo1 {
public static void main(String[] args) {
Outer1.Inner1 inner = new Outer1().new Inner1();
System.out.println(inner.num2);
}
}
3 静态内部类
静态内部类:(外部类所有,成员位置)
a.内部类访问外部类成员:
直接访问,包括私有成员,但是只能访问静态的。
注意:内部类成员隐藏外部类成员怎么办?
只能通过外部类名去访问,不能使用Outer.this,因为静态内部类中不能访问非静态的this。
b.外部类访问内部类成员:
创建内部类对象,通过该对象访问,包括内部类私有成员。
c.其他类访问内部类成员:
创建内部类对象,Outer.Inner inner = new Outer.Inner(),通过该对象访问,并受访问权限限制。
public class Outer2 {
private int a = 10;
public int b = 20;
private static int c = 30;
public static int d = 40;
static class Inner2 {
int c = 300;
int d = 400;
private int num1 = 1;
public int num2 = 2;
public void show() {
System.out.println(c);
System.out.println(d);
System.out.println(Outer2.c);
System.out.println(Outer2.d);
}
}
public static void main(String[] args) {
Inner2 inner = new Inner2();
inner.show();
System.out.println(inner.num1);
System.out.println(inner.num2);
}
}
其他类访问静态内部类成员:创建对象,受访问权限限制
public class Demo2 {
public static void main(String[] args) {
Outer2.Inner2 inner = new Outer2.Inner2();
System.out.println(inner.num2);
}
}
4 局部内部类
局部内部类:
位置:局部位置,方法内或方法声明上,相当于局部变量。
a.内部类访问外部类:
直接访问,并可以访问外部类私有成员。
访问局部变量:直接访问,但是该局部变量必须是final修饰的,如果没有添加final,系统会默认添加上final。
b.外部类访问内部类:
创建内部类对象,并且可以访问内部类的私有成员,但是只能在方法内部访问,因为局部内部类相当于局部变量,方法外不可访问。
c.其他类访问内部类:
不能!局部内部类只能在方法中被访问。
问题:为什么内部类访问外部类的局部变量时,该局部变量不能发生改变呢?
因为该局部变量必须是final修饰,如果没有,系统会默认添加上final。
对下面的Outer3编译后生成的字节码文件反编译之后,得到:
class Outer3$1Inner3 {
final int val$num1;
final Outer3 this$0;
Outer3$1Inner3() {
this$0 = final_outer3;
val$num1 = I.this;
super();
}
}
发现在局部内部类中会把局部变量num1声明为final修饰的成员变量。
public class Outer3 {
private int a = 10;
public int b = 20;
public void show() {
int num1 = 100;
class Inner3 {
private int num = 300;
public int num2 = 200;
public void method() {
System.out.println(a);
System.out.println(b);
System.out.println(num1);
System.out.println(num2);
a = 1;
b = 2;
num2 = 4;
System.out.println(a);
System.out.println(b);
System.out.println(num1);
System.out.println(num2);
}
}
Inner3 inner = new Inner3();
inner.method();
System.out.println(inner.num);
System.out.println(inner.num2);
}
public static void main(String[] args) {
Outer3 outer = new Outer3();
outer.show();
}
}
如果不把局部变量声明成final类型,则程序可能出现问题,看下面的例子就知道了:
如果局部变量a没有final修饰,那么a会在栈中,outer.getSing()方法返回Inner4对象后,栈释放,a的内存也释放,之后再调用sing()方法,由于多态,会访问局部内部类Inner4中的sing()方法,需要打印a,这时由于a的内存已释放,无法访问到,所以报错。但是将局部变量a默认用final修饰后,变量a就会存放到堆中的常量池,这样尽管getSing()栈释放后,再在sing()中访问a,直接去常量池中访问,程序不会报错。注意:JDK1.7以前,常量池在方法区中,JDK1.7开始,把常量池放到了堆中。
关于局部内部类的另外两个代码案例:
interface Sing {
void sing();
}
public class Outer4 {
public Sing getSing() {
int a = 10;
class Inner4 implements Sing {
@Override
public void sing() {
System.out.println("a = " + a);
}
}
return new Inner4();
}
public static void main(String[] args) {
Outer4 outer = new Outer4();
Sing sing = outer.getSing();
sing.sing();
}
}
public class Outer5 {
int a = 1;
public void show() {
int a = 10;
class Inner5 {
int a = 100;
public void method() {
int a = 1000;
System.out.println(a);
System.out.println(this.a);
System.out.println(Outer5.this.a);
}
}
Inner5 inner = new Inner5();
inner.method();
}
public static void main(String[] args) {
new Outer5().show();
}
}
5 匿名内部类
匿名内部类:
前提:存在一个类或者接口。
类:可以是普通类或抽象类,但是不能是final修饰的最终类,因为匿名内部类相当于创建该类的子类对象,最终类不能被继承,没有子类。
接口:相当于创建该接口的实现类对象。
格式:new 类名或接口名() {重写方法;}
本质:创建了一个继承或实现该类或接口的子类对象。
new Sing() {
@Override
public void sing() {
System.out.println("sing,sing,sing...");
}
};
我要创建一个Sing类型的对象, 发现它是一个接口, 需要它的子类, 子类还没有定义,则可以使用匿名内部类对这个子类进行定义。
interface Sing2 {
void sing();
}
class Dance {
public void dance() {
}
}
public class Outer6 {
public Sing2 getSing() {
return new Sing2() {
@Override
public void sing() {
System.out.println("sing, sing, sing...");
}
};
}
public Dance getDance() {
return new Dance() {
@Override
public void dance() {
System.out.println("dance, dance, dance...");
}
};
}
public static void main(String[] args) {
Outer6 outer = new Outer6();
Sing2 sing = outer.getSing();
sing.sing();
Dance dance = outer.getDance();
dance.dance();
}
}
6 练习
练习一:补齐程序(注意:内部类和外部类如果没有使用extends,则没有继承关系)
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(...);
System.out.println(...);
System.out.println(...);
}
}
}
使得在控制分别输出:30,20,10。
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(num);
System.out.println(this.num);
System.out.println(Inner.this.num);
System.out.println(Outer.this.num);
}
}
}
public class Test1 {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.show();
}
}
练习二:看程序写结果
abstract class Person {
public abstract void show();
}
class PersonDemo {
public Person method() {
return new Person() {
@Override
public void show() {
System.out.println("show");
}
};
}
}
public class Test2 {
public static void main(String[] args) {
PersonDemo pd = new PersonDemo();
Person person = pd.method();
person.show();
}
}
练习三:按照要求,补齐代码
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台输出"HelloWorld"
interface Inter2 {
void show();
}
class Outer2 {
public static Inter2 method() {
return new Inter2() {
@Override
public void show() {
System.out.println("Hello world");
}
};
}
}
public class Test3 {
public static void main(String[] args) {
Outer2.method().show();
}
}
7 作业
public class Test {
public static void main(String[] args){
Test.Bean1 bean1 = new Test().new Bean1();
bean1.I++;
Test.Bean2 bean2 = new Test.Bean2();
bean2.J++;
Bean.Bean3 bean3 = new Bean().new Bean3();
bean3.K++;
System.out.println(bean1.I);
System.out.println(bean2.J);
System.out.println(bean3.K);
}
class Bean1 {
public int I = 0;
}
static class Bean2 {
public int J = 0;
}
}
class Bean {
class Bean3 {
public int K = 0;
}
}
|