内部类
概述
如果一个事物的内部有同事包含另一个事务,那么这就是一个类的内部包含了另一个类。
列如:身体和心脏的关系 “has - a”包含关系。(心脏属于身体的一部分,身体包含心脏)
汽车和发动机的关系 “has - a”包含关系。 (发动机属于汽车的一部分,汽车包含发动机)
-
定义
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类.
分类
成员内部类
-
定义在类中方法外的类
-
格式
修饰符 class 外部类名称{ //... 修饰符 class 内部类名称{ //... } } 在描述事物时,如果一个事物内部包含了另外的其他事物,就可以使用内部类这种内部类这中接口。定义在 【成员变量的位置】。 //内部类访问外部类的信息随意访问,外部类访问内部类的信息,需要内部类对象。
-
使用成员内部类有两种方式
1. 间接方式: 在外部类的方法当中,使用内部类 2. 直接方式: 格式: //1 外部类名.内部类类名 对象名 = new 外部类名().new 内部类名(); //2 内部类类名 对象名 = new 外部类名().new 内部类名();//需要导包 import 包名.外部类名.内部类名;
// 如果出现重名现象,那么格式是:外部类名称.this.外部类的成员变量名
public class OuterClass {
String name = "小刘";// 外部类的成员变量
public class InnerClass {
String name = "小花";// 内部类的成员变量
public void methodInner(){
String name = "小王";// 内部类方法内部的局部变量
System.out.println(name);// 小王 局部变量 就近原则
System.out.println(this.name);// 小花 内部类的成员变量
// 狸猫换太子
System.out.println(new OuterClass().name);// 小刘 外部类的成员变量
}
}
// 间接方式
public void methodOuter() {
System.out.println("外部类的方法在加载。。。。");
InnerClass inner = new InnerClass();
inner.methodInner();// 调用内布列方法
}
}
局部内部类 (包含匿名内部类)
-
定义在类的方法的内部“局部”只有当前所属的方法内才能使用它,出来这个方法外面就不能用了
-
格式
修饰符 class 外部类名称{ //方法 修饰符 返回值类型 外部类方法名称 (参数列表){ class 局部内部类名称{ //。。。 } } }
public class OuterClass { // 外部类 // 定义外部类方法 public void methodOuter() { final int num;// 所在方法的局部变量 默认是有final关键字修饰的 num = 20; // 定义局部内部类 class InnerClass { // 定义局部内部类的方法 public void methodInner() { System.out.println(num);// 10 } } // 在方法内部使用它 InnerClass innerClass = new InnerClass(); innerClass.methodInner(); } } //调用 public static void main(String[] args) { // 输出num值 // 调用methodInner() // 实例化一个外部类的对象 OuterClass outerClass = new OuterClass(); outerClass.methodOuter(); }
局部内部类访问外部类的方法中的局部变量
在局部内部类的方法中想要访问方法的局部变量的话,要求此局部变量声明为final的。
jdk 7及之前版本:要求此局部变量显式的声明为final的
jdk 8及之后的版本:可以省略final的声明
原因:
1.new出来的对象保存在对用的内存当中的
2.局部变量是随着方法的压栈而在内存当中存储
3.方法运行结束后,立刻弹栈,局部变量在内存当中消失了
4.但是new出来的对象并不会随着方法的弹栈而立刻消失,知道JVM把他认为是垃圾,才会回收消失掉。
* public > protected > (default) > private
* 类的权限修饰符:
* 定义一个类的时候,权限修饰符的规则:
* 1. 外部类: public 、(default)
* 2.成员内部类: public 、 protected、(default)、private
* 3.局部内部类: 什么都不能写
匿名内部类【重要】
概念:
是内部类的简写。他的本质其实是一个 带着具体实现父类或者接口的匿名的 子类对象
开发中,最常用到的内部类是【匿名内部类】
前提
匿名内部类必须【继承一个父类】或者【实现一个接口】,伴随着重写父类或者父接口当中的抽象方法
格式
new 父类名或者父接口名{
//方法的重写
@Oberride
public void method(){
//重写的方法体内容
}
};
//通常在方法的形参参数是接口或者抽象类时,一般将匿名内部类作为实参进行传递
//定义一个接口
public interface Animal{
public abstract void eat();
}
//定义一个启动类
public class Text{
public static void showEat(){
animal.eat();
}
public static void main(String[] args){
//采用匿名内部类的方式来写
//多态写法
//等号左边 接口
//等号右边 本质是Animal接口的子类对象
Animal a = new Animal(){
@Override
public void eat(){
System.out.println("猫吃鱼");
}
};
showEat(eat);//猫吃鱼
//简化
//匿名对象
showEat(new Animal(){
@Override
public void eat(){
System.out.println("猫吃鱼");
}
});
}
}
对格式<<new 接口名称 (){}>>分析
* 对格式<<new 接口名称 (){}>>分析
* 1.new代表创建对象
* 2.接口名称就是匿名内部类实现的那个接口,同时需要创建那个接口实现类的对象
* 3.{}这是匿名内部类的内容
*
* 使用匿名内部类注意事项:
* 1.匿名内部类,在【创建对象】时,只能使用唯一一次
* 如果希望对此创建对象,而且类的内容是一样大的,建议单独定义实现类。
* 2.匿名对象,在【调用方法】时,也只能使用唯一一次
* 如果希望同一个对象调用多次方法,那么建议给对象起个名字
* 3.匿名内部类省略了【实现类/子类名称】,但是匿名对象是省略了【对象名】
* 备注;匿名内部类和匿名对象两个根本就不是一回事!
总结
成员内部类和局部内部类,在编译以后,都会生成字节码文件。
格式: 成员内部类:外部类$内部类名.class
局部内部类:外部类$数字内部类名.class
静态内部类
- 用static修饰的内部类,称为静态内部类,完全属于外部类本身,不属于外部类某一个对象
- static关键字的作用是把修饰的成员变成类相关,而不是实例相关
- 静态内部类可以包含静态成员,也可以包含非静态成员,但是在非静态内部类中不可以声明静态成员。
- 静态类内部不可以访问外部类的实例成员,只能访问外部类的类成员,即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员
- 外部类不可以定义为静态类,Java中静态类只有一种,那就是静态内部类,顶级类不能用static 修饰
- 外部类如何调用静态内部类中的属性和方法
- 外部类可以通过创建静态内部类实例的方法来调用静态内部类的非静态属性和方法
- 外部类可以直接通过“ 外部类.内部类.属性(方法)” 的方式直接调用静态内部类中的静态属性和方法 - 如何创建静态内部类实例
- 在非外部类中:外部类名.内部类名 name = new 外部类名.内部类名();
- 在外部类中:内部类名 name = new 内部类名();
public class Outter {
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
protected class Inner {
public Inner() {
}
}
}