内部类
- 1 成员内部类
- 2 静态内部类
- 3 匿名内部类
- (1)Thread类的匿名内部类实现
- (2)Runnable接口的匿名内部类实现:
- 4 局部内部类
内部类本身就是类的一个属性,与其他属性定义方式一致。
1 成员内部类
定义在类内部的非静态类,就是成员内部类。成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,所以成员内部类不能定义静态方法和变量(final 修饰的除外)。
(1)获取成员内部类的对象?
外部类类名.内部类类名 变量 = 外部类对象的引用.new 内部类()
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass =
outerClass.new InnerClass();
或者采用:
OuterClass.InnerClass innerClass =
new outerClass().new InnerClass();
new outerClass()相当于一个匿名类,即这个外部类没有名字。
(2)在实例内部类当中,不能定义static的属性或者方法
static属于类,不依赖于对象,而内部类相当于外部类的实例成员,要想对内部类进行初始化,必须依赖对象,所以这是矛盾的。 如果非要定义,那么这个属性必须是static final的,被static final 修饰的属性相当于常量,在编译期间就确定了。
(3)实例内部类有没有额外的内存开销?
有,因为实例内部类,包含外部类的this引用
实例内部类至少有两个this。
实例内部类代码举例:
内用外,随便访问;外用内,需要内部类对象
class OuterClass {
public int data1 = 1;
private int data2 = 2;
int data3 = 3;
public static int size = 10;
public OuterClass() {
System.out.println("OuterClass()");
}
//通过外部类方法,访问内部类
public void outerMethod(){
InnerClass inner = new InnerClass();
inner.test();
}
class InnerClass {
public int data4 = 4;
//public static final int size = 10;
public int data1 = 11;
public InnerClass() {
System.out.println("InnerClass()");
}
public void test() {
System.out.println("data1:"+data1);
System.out.println("data1:"+this.data1);
//在内部类中访问外部类成员变量
System.out.println("out::data1:"+
OuterClass.this.data1);
System.out.println("InnerClass::test()");
}
}
}
public class Main{
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass =
outerClass.new InnerClass();
//以上两行代码可以使用匿名类替换为一行代码
//OuterClass.InnerClass outerClass =
//new OuterClass().new InnerClass();
//通过直接创建内部类对象,访问内部类
innerClass.test();
//通过访问外部类方法,间接访问内部类
outerClass.outerMethod();
}
}
结果:
OuterClass()
InnerClass()
data1:11
data1:11
out::data1:1
InnerClass::test()
InnerClass()
data1:11
data1:11
out::data1:1
InnerClass::test()
注意:
在内部类中访问外部类的同名变量OuterClass.this.data1
2 静态内部类
定义在类内部的静态类,就是静态内部类。
public class Outer {
private static int a = 1;
static class StaticInner {
public void visit() {
System.out.println("static");
}
}
}
- 静态内部类可以访问外部类所有的静态变量和方法,即使是private 的也可以。
- 静态内部类和一般类一致,可以定义静态变量、方法,构造方法等。
- Java集合类HashMap 内部就有一个静态内部类Entry。HashMap 内部维护Entry 数组用于存放元素,但是Entry 对使用者是透明的。像这种和外部类关系密切的,且不依赖外部类实例的,都可以使用静态内部类。
(1)如何拿到静态内部类的对象?
外部类类名.内部类类名 变量 = new 外部类名.内部类();
Outer.StaticInner staticInner =
new Outer.StaticInner();
(2)静态内部类当中,不能够访问外部类的非静态数据成员
外部类的非静态数据成员依赖于外部类对象,而静态内部类相当于外部类的一个静态成员,它是不依赖于对象的,所以在静态内部类中,不能够访问外部类的非静态数据成员。如果说,非要进行访问非静态数据成员,那么就要传入外部类对象的引用。 同理,在静态内部类中,也不包含外部类的this。
静态内部类代码实例:
class OuterClass {
public int data1 = 1;
private int data2 = 2;
int data3 = 3;
public static int data5 = 10;
public OuterClass() {
System.out.println("OuterClass()");
}
static class InnerClass {
public int data1 = 4;
public static int data5 = 5;
//outerClass记录传入外部类对象的引用
OuterClass outerClass;
public InnerClass() {
System.out.println("static::InnerClass()");
}
//定义一个内部类的有参构造,来接收外部类的对象
public InnerClass(OuterClass o) {
this.outerClass = o;
System.out.println("static::InnerClass(OuterClass o)");
}
public void test() {
System.out.println("data1:" +
this.outerClass.data1);
System.out.println(data1);
//若没有传入外部类的对象引用,
//是不能在静态内部类中调用外部类的成员变量的
//System.out.println(data3)
System.out.println("data5:"+data5);
System.out.println("data5:" + OuterClass.data5);
System.out.println("InnerClass::test()");
}
}
}
public class Main{
public static void main(String[] args) {
//此句是为了向静态内部类中传入外部类对象创建的
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = new
OuterClass.InnerClass(outerClass);
// OuterClass.InnerClass innerClass = new
//OuterClass.InnerClass();
innerClass.test();
}
}
结果:
OuterClass()
static::InnerClass(OuterClass o)
data1:1
4
data5:5
data5:10
InnerClass::test()
3 匿名内部类
- 本质:匿名内部类会隐式的继承一个类或者实现一个接口,或者说,匿名内部类是一个继承了该类或者实现了该接口的子类匿名对象。
- 接口的实现类或父类的子类只需使用一次,那么即可省去该实现类或子类的定义,匿名内部类主要应用于线程中。
- 使用匿名内部类,就不必再单独定义一个外部实现类了。
格式:
new 类名/接口/抽象类(){
}
不使用匿名内部类:
class OuterClass{
public int data1 = 10;
public void test() {
System.out.println("OuterClass3::test()");
}
}
class Inner extends OuterClass {
public void test() {
System.out.println("do something");
}
}
public static void main(String[] args) {
OuterClass inner = new OuterClass();
inner.test();
}
如果Inner类只使用一次,那么将其编写为独立的一个类就会变得很麻烦,因此我们引入了匿名内部类。匿名内部类就是没有名字的内部类:
class OuterClass{
public int data1 = 10;
public void test() {
System.out.println("OuterClass3::test()");
}
}
public static void main(String[] args) {
new OuterClass(){
//重写test方法
public void test() {
System.out.println("TestDemo1::test()");
}
}.test();//TestDemo1::test()
}
匿名内部类可以省略一个类的书写,并且,匿名内部类还能用于接口上。
最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是实现Runnable接口:
(1)Thread类的匿名内部类实现
public class Main {
private static class A extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
}
public static void main(String[] args) {
A a = new A();
a.start();
}
}
匿名内部类:
public class Demo {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
t.start();
}
}
(2)Runnable接口的匿名内部类实现:
public class Main {
private static class A implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
}
public static void main(String[] args) {
A a = new A();
Thread t = new Thread(a);
t.start();
}
}
匿名内部类:
public class Demo {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
Thread t = new Thread(r);
t.start();
}
}
注意事项:
- 匿名内部类不能定义任何静态成员和静态方法。
- 只针对重写一个方法时使用,需要重写多个方法时不建议使用
4 局部内部类
定义在方法内部的类,只能在方法内部使用。
局部内部类如果希望访问所在方法的局部变量,那么这个局部变量必须是有效final的?
new出来的对象在堆内存中,直到垃圾回收才消失。局部变量和方法一起在栈内存中,方法结束后,局部变量就会消失。 为了保证new出来的对象,继续使用局部变量,故需要该局部变量是有效不变的。
局部内部类的创建方式,在对应方法内: new 内部类()
public class Outer{
public void methodOuter{
int num = 10;//此处必须保证是实际不变的
//int final num = 10;
class Inner{
public void methodInner(){
System.out.println(num);
}
}
Inner inner = new Inner();
}
}
定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。