内部类知识点
——记于2018年1月15日12:33:25
内部类定义
内部类的定义,可以在任何地方;
例如,可以定义在方法内部;这里你需要知道一个事实:即使这个内部类被定义在方法中,但是在编译时期,内部类即被编译了;
内部类与外部类的通信
内部类对象,可以直接访问外部类的所有成员;反过来的话,需要创建内部类对象,通过对象访问;有个特殊的地方,在外部类中是可以访问到内部类的 私有成员
的;
内部类的一些细节
- 必须使用外部类对象来创建内部类对象;
- 通常使用内部类来实现接口,达到隐藏实现细节的效果;
- 匿名内部类中,需要用到外部变量的时候,外部变量需要被修饰为
fianl
; - 匿名内部类实现构造器效果;使用场景:有的时候,我们需要在匿名内部类中,进行赋值操纵;常规操纵是写一个匿名类,进行赋值;这里,可以用类似构造器的效果实现这个功能,更简洁;
- 内部类(并不是所有的内部类,比如局部内部类)可以有多种修饰权限,
public
、private
、protected
、包访问权限
;这和普通类仅有两种权限:public
、包访问权限
不一样; 嵌套类
:内部类被修饰为static
;
嵌套类
和 普通内部类
的区别:
- I、普通内部类对象隐式的持有一个外部类对象的引用;嵌套类则没有;
- II、创建嵌套类对象是不需要外部类对象的引用的,可以直接创建嵌套类;普通内部类则相反;
- III、不能从嵌套类的对象中,访问非静态的外部类对象;
- IV、普通内部类是不可以有static字段和数据的;因此,也就不可以含有嵌套类;但是,嵌套类则反之;
接口
中放在 嵌套类
接口中是可以放置 class
的;
因为接口中所有数据都被修饰为 public
static
;这与嵌套类不谋而合,因此,接口中可以放置嵌套类进去;
使用场景:
a、将公用代码放置到接口的嵌套类中,即可实现代码的复用;
b、将测试代码放到嵌套类中,因为,内部类会单独的生成一个类文件出来,这样,我们就可以运行这个单独的类。
内部类的继承
因为,内部类的创建,必须有外部类对象来创造,也就是必须获得先创建外部类对象,再创建内部类对象;
因此,继承类的构造器,需要接受一个外部类对象的引用,我们用这个引用调用外部类的构造器,创建外部类的对象,让这个外部类对象创建我们的继承的内部类的对象;
内部类的覆盖
这个我看书,也没看出答案来,好像是可以覆盖,但是没有实际意义;
代码演示
import org.junit.Test;
/**
* Created by Yaz on 2018/1/15.
*/
public class Outter {
private int i = 9;
class Inner {
----------------------------1、内部类与外部类的通信-----------------
private int num = 1;
public Inner() {
// 内部类拥有外部类的所有访问权;就是说,内部类可以访问外部类的任何字段,方法。不受限制(无视private);
// 原因:当某个外部类对象,创建内部类对象的时候,内部类对象会秘密的捕获一个指向创建它自己的那个外部类的对象的引用;(java编程思想 - P192)
i = 10;
System.out.println("创建内部类对象 i=" + i);
}
}
int getInnerNum() {
// 外部类对象访问内部类的成员,需要通过类、或者创建内部类对象来访问
// 有个特殊的地方,外部类是可以访问到内部类的私有成员的;
int num = new Inner().num;
return num;
}
----------------------------2、必须使用外部类对象创建内部类对象-----------------
// 内部类于外部类而言,就像是一个普通字段;
// 因此,这里可以,直接使用内部类作为返回值类型
Inner getInner() {
// 创建非嵌套的内部类对象(即非static的),必须有外部类对象的引用,也就是,必须使用外部类对象创建内部类对象
return new Inner(); // 这里的外部类对象引用是this,所有完整的写法也就是 return this.new Inner();
}
public static Inner getInner2() {
Outter outter = new Outter();
// 在静态方法中,是没有this的,因此,需要手动创建外部类对象,
Inner inner = outter.new Inner();
return inner;
}
--------------------------3、必须使用外部类对象创建内部类对象----------------
/**
* 在获取返回值的时候,向上提升类型了(类的名字,无从得知),,并且Cat的内部类是private,
* 因此,即使你获得 Animal,你也不能向下转型了,你根本就不知道这是个Cat,所有Cat类中的方法的实现细节,谁也不知道了
*
* @return Animal
*/
public Animal getAnimal() {
return new Cat();
}
private class Cat implements Animal {
@Override
public void eat() {
// 方法的实现细节,没人知道了
System.out.println("猫吃鱼");
}
@Override
public void sleep() {
System.out.println("随便睡");
}
@Override
public void play() {
System.out.println("女主人");
}
}
------------------------4、final修饰匿名内部类中用到的外部变量----------------
// 匿名内部类
public NoName getNoName(final String name) {
return new NoName() {
void aha() {
// 匿名内部类中用到的参数是外部传进来的,我们需要修饰这个参数为final;
System.out.println(name);
}
};
}
---------------------------5、为匿名内部类实现构造器效果-----------------
// 匿名内部类
public NoName getNoName(final String name, final int age) {
return new NoName() {
// 这里需要注意name的生命周期的问题
// name是匿名内部类外部定义的,因此,当匿名内部类中定义相同的name时,
// 会发现隐藏变量现象;
private String _name = name;
@Override
void aha() {
System.out.println(name + ":" + age);
}
};
}
@Test
public void test1() {
// 有了上面的方法,我们就可在方法中实现构造器的效果了;
NoName noName = getNoName("yaz", 22); //构造器效果
noName.aha();
}
--------------------------9、内部类的继承-----------------
class Mary {
}
//继承内部类Mary
class SonOfMary extends Outter.Mary {
SonOfMary(Outter outter) {
// 这个外部类引用,必须有
// 让它先创建外部类对象,让外部类对象来创建继承的内部类对象,
// 最后才是继承的类SonOfMary对象的创建;
outter.super();
}
}
}
//定义一个动物接口,我们用内部类实现这个接口
interface Animal {
void eat();
void sleep();
void play();
}
//抽象类,不一样非要继承实现,也可以用匿名内部类(省略了一个向上转型的动作)
abstract class NoName {
abstract void aha();
}