1. 内部类是什么?为什么存在内部类?
将一个类放到另一个类 内部的嵌套操作,这个类就称为内部类。
大家可能和我有同样的疑问,为什么要有内部类的产生呢?经过时代的发展,我们都会发现,每一样的新东西的出现,都是是我们的操作更加便利,所以,我们在不看任何的资料前提下,就可以菜刀,内部类的产生是为了某种操作的便利。
下面我们将看一段代码来分析,使用内部类和不使用内部类实现相同功能代码的优缺点。
1.1 不使用内部类
// 没有采用内部类
class Outter{
private String msg = "Outter中的字符串";
public String getMsg(){
return this.msg;
}
public void test(){//2
//生产内部类对象
Inner in = new Inner(this);//3
in.fun();//5
}
}
class Inner{
private String inMsg = "Inner中的字符串";
private Outter out;
//构造注入
public Inner(Outter out){//3
this.out = out; //4 为Inner中的out变量初始化
}
public void fun(){//6
//直接调用外部类的私有属性
System.out.println(out.getMsg());//7
}
}
public class Test{
public static void main(String[] args){
Outter out = new Outter();//1
out.test();//2
}
}
通过分析代码可以知道,我们创建Outter类的对象out,然后调用out类的对象调用了test()方法,我们进入到test()方法中会发现它里面通过构造注入将该对象传入到Inner类中创建in对象,然后in对象调用了fun()方法,fun()方法又调用了通过out对象调用了getMsg()方法才打印了out方法的msg属性。在几次折腾下终于成功的调用了此方法。
分析到这里我们就会想到,我们其实事项访问Outter类的私有属性,那么我们会想到一个身体上的实例,心脏是在人身体内部工作的,血液可以在心脏内部进行流动,那么我们把Outter类看作是身体,把Inner类看作为心脏会有什么效果产生呢?
1.2 使用内部类
// 采用了内部类
class Outter{
private String msg = "Outter中的字符串";
//-------------------------------------
//内部类
class Inner{
private String inMsg = "Inner中的字符串";
public void fun(){
// 发现内部类可以直接调用外部类的私有属性
System.out.println(msg);
}
}
//-------------------------------------
//在外部类中定义一个方法,该方法负责产生内部类对象,并调用fun()方法
public void test(){
// 生产内部类对象
// 与之前不使用内部类不同的是,不用通过构造注入的方法将当前的out对象传入Inner类就可以实现直接调用out对象的属性及方法
Inner in = new Inner();
in.fun();
}
}
public class Test{
public static void main(String[] args){
Outter out = new Outter();
out.test();
}
}
我们可以发现两种代码实现相同的功能,不适用内部类的代码比较难以分析,调来调去,还需要构造注入外部对象才可以访问其public权限的属性和方法,但是内部类就比较简洁,最根本的原因是内部类可以直接访问外部类的私有属性。
如果你想不明白的话,可以想像,有一间房为Outter,它里面的厕所对外部人员不可见,但是你身处在房子里面为Inner,这个厕所你就可以进去操作。这种的就可以看作是封装,内部操作对外部而言不可见,实现封装;内部类可以直接访问外部类的私有属性。
2. 内部类实现多继承
Java的单继承局限:只能继承一个类,但可以实现多个接口。 如果我要打破这个局限,除了使用多层继承之外,还可以使用内部类进行操作。
// 内部类实现多继承
class A
{
private String name="A类私有域";
public String Getname(){
return name;
}
}
class B
{
private int age=18;
public int Getage(){
return age;
}
}
class C
{
//ExtendA 继承A
class ExtendA extends A{
public String Name()
{
return new A().Getname(); // 返回父类A的属性name
}
}
// ExtendB 继承B
class ExtendB extends B{
public int Age(){
return new B().Getage();//返回父类B的属性年龄
}
}
public String name(){
return new ExtendA().Name(); //返回内部类的Name,相当于A的name
}
public int age(){
return new ExtendB().Age(); //返回内部类的Age,相当于B的age
}
//C类现在既有A的属性name,也有B的属性age,模拟实现了多继承
}
public class Test{
public static void main(String[] args)
{
C c=new C();
System.out.println(c.name()); // A类的私有域
System.out.println(c.age()); //18
}
}
3. 内部类的优缺点
优点:
- 内部类和外部类可以方便的访问彼此的私有域(包括私有方法、私有属性)。
- 内部类是另外一种封装(保护性),对外部的其他类隐藏 —>例如:心脏包在人体内部
- 内部类可以实现Java单继承的局限
缺点: 结构较为复杂,破坏了类的结构性。
4.内部类与外部类的关系
- 对于非静态内部类,内部类的创建需要依赖外部类对象,在没有外部类实例之前无法创建非静态内部类。
- 内部类是一个相对独立的个体,与外部类没有is-a关系
- 内部类可以直接访问外部类的元素(包含私有域),但是外部类不可以直接访问内部类元素,需要通过内部类的引用间接访问。
5. 创建内部类
5.1 在外部类内部创建内部类语法
外部类.内部类 内部类引用 = new 外部类().new 内部类();
Outter.Inner in = new Outter().new Inner();
5.2 在外部类内部创建内部类语法
在外部类内创建内部类,就像普通对象一样直接创建。
Inner in = new Inner();
6. 内部类的分类
在Java中内部类主要分为成员内部类、静态内部类、方法内部类、匿名内部类
6.1成员内部类
成员内部类同成员方法一样。
注意:
- 成员内部类不能存在任何static变量或方法,可以访问外部类的静态域。
- 成员内部类是依附外部类的,所以只有先创建了外部类才能创建内部类。
举例:
class Outter{
private String name = "test";
private int age = 20;
class Inner{
//成员内部类内部不允许出现静态变量,它类似于成员方法,
//而静态变量的生命周期与类相同,不需要创建实例便可引用,
//但成员方法必须先创建实例才可使用
// private static int num = 50; //本行代码错误
public void fun(){
System.out.println(name);
System.out.println(age);
}
}
}
6.2 静态内部类
关键字static可以修饰成员变量、方法、代码块,其实它还可以修饰内部类。使用static修饰的内部类,称为静态内部类。
静态内部类与非静态内部类的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围,但是静态内部类没有。因为没有这个引用,所以隐含着下面两条信息。
重点:
- 静态内部类的创建不需要依赖外部类,可以直接创建。
- 静态内部类不可以使用任何外部类的非static域(包含属性与方法),但可以存在自己的成员变量。
静态内部类的创建语法:
外部类.内部类 内部类对象 = new 外部类.内部类();
Outter.Inner in = new Outter.Inner();
举例:
class Outter{
private String name = "test";
private int age = 20;
static class Inner{
public void fun(){
//下面两句话均错误,静态内部类类似于静态方法,它的声明周期类似于一个类
//静态方法不需要实例化对象,便可通过类名引用,
//但成员变量必须实例化对象后,通过对象才可引用。
System.out.println(name);
System.out.println(age);
}
}
}
6.3 方法内部类
方法内部类定义在外部类的方法中,局部内部类和成员内部类基本一致,只是它们的作用域不同,方法内部类只能在该方法中被使用,出了该方法就会失效。 对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。
- 方法内部类不允许访问权限修饰符public、private、protected均不允许。
class Outter{
private int num = 5;
public void display(int temp){
//方法内部类:方法内部定义的
//下面这行代码(关于方法内部类的定义):方法内部类不能使用任何的访问修饰权限
public class Inner{
}
}
}
- 方法内部类对外部完全隐藏,除了创建这个类的方法可以访问它以外,其他地方均不能访问。
- 方法内部类如果想要使用方法形参,该形参必须使用final声明(JDK8将形参变为隐式final声明)。
class Outter{
private int num = 5;
//方法内部类如果想要使用方法形参,该形参必须使用final声明
//(JDK8将形参变为隐式final声明)。
public void display(int temp){
//方法内部类:方法内部定义的
class Inner{
public void fun(){
System.out.println("外部类中的程序变量:" + num);
System.out.println("方法的形参:" + temp);
}
}
//使用方法内部类:只能在方法内部,外部根本不知道方法内部存在一个类
new Inner().fun();
}
}
//测试
public class Test{
public static void main(String[] args){
Outter out = new Outter();
out.display(18);
}
}
如何判断方法的形参为final声明的?—> final被称为终结器,修饰的变量为常量,不能修改。
class Outter{
private int num = 5;
public void display(int temp){
//方法内部类:方法内部定义的
class Inner{
public void fun(){
temp++;
System.out.println("外部类中的程序变量:" + num);
System.out.println("方法的形参:" + temp);
}
}
//使用方法内部类:只能在方法内部,外部根本不知道方法内部存在一个类
new Inner().fun();
}
}
public class Test{
public static void main(String[] args){
Outter out = new Outter();
out.display(18);
}
}
6.4 匿名内部类
匿名内部类其实就是一个没有名字的方法内部类。因此特点与方法内部类完全一样,除此之外,还有两个自己的特点。
- 匿名内部类必须继承一个抽象类或者实现一个接口。
- 匿名内部类没有构造方法(因为没有类名)
//声明定义一个接口
interface MyInterface{
void test();
}
class Outter{
private int num = 5;
public void display(int temp){
//匿名内部类,匿名的实现了MyInterface接口
new MyInterface(){
public void test(){
System.out.println("匿名内部类实现了MyInterface接口");
System.out.println(temp);
}
}.test();
}
}
public class Test{
public static void main(String[] args){
Outter out = new Outter();
out.display(18);
}
}