内部类
在一个类Outer的内部定义一个类Inner,此时Outter称为外部类,Inner称为内部类。类的定义是可以多层嵌套的。即在类的内部进行其他类结构的嵌套操作。
内部类中定义的成员要比外部定义的成员具有更严格的隐藏信息功能。
内部类的作用:
- 内部类的对象可以访问外部类的所有属性和方法,包括私有的,因为内部类被当作了外部类的成员,同一个类的成员可以相互访问。但是外部类不能直接访问内部类中的实现细节,这一特点弥补了继承的缺憾。
class Outer{
private String msg = "这是写在外部类的私有属性";
//定义一个成员内部类
class Inner{
int num=2;
public void print(){
//打印外部类的私有属性
System.out.println(msg);
}
}
public void fun(){
Inner in =new Inner();
in.print();
}
}
public class InnerTest{
public static void main(String[] args){
//创建外部类对象,调用内部类方法
Outer ou =new Outer();
ou.fun();
}
}
2. 内部类提供了更好的封装,可以隐藏在外部类之中,不被同一个包的其他类所见。
3. 匿名内部类可以方便 的定义运行时回调和用于仅需要一次使用的类
顶级类(最外层的类)只有两种访问控制权限public与默认;内部类有四种访问控制权限:public protected 默认 private
4. 对于非静态内部类,内部类的创建需要依赖外部类对象,在没有外部类实例之前无法创建非静态内部类。(内部类中的属性前默认添加 外部类.this.
)
5. 内部类时一个相对独立的个体,与外部没有is-a关系
class Outer{
//定义外部类的私有属性
private String s="this is 外部类";
//声明内部类
public class Inner{
//定义内部类的方法
public void add(int x,int y){
System.out.println(x+"+"+y+"="+(x+y));
}
public void getStr(){
//内部类中直接访问外部类成员属性
System.out.println(s);
}
}
//从外部调用内部类的方法
public void getInofo(){
//匿名内部类对象调用外部类方法
new Inner().getStr();
//声明创建内部类实例对象
Inner in = new Inner();
//实例调用内部类方法
in.add(7,8);
}
}
public class InnerTest2{
public static void main(String[] args){
//在其他类中创建外部类的实例对象
Outer out=new Outer();
//外部类实例调用外部类的方法
out.getInofo();
//在其他类中创建内部类的实例对象
Outer.Inner in = new Outer().new Inner();
//调用内部类的方法
in.add(1,2);
in.getStr();
}
}
内部类的分类
成员内部类是依附于外围类的,只有先创建了外围类才能创建内部类
(1)实例内部类
实例内部类是指声明在外部的方法体外的,即与外部类的成员(属性、方法)同级。没有static修饰的内部类。
实例内部类的特点
- 在实例内部类中不能存在任何静态的变量或方法。
- 在外部类的静态方法或外部类以外的其他类,若需要访问内部类,则必须通过外部类创建内部类的访问实例访问(内部类依赖于外部类的对象)。
外部类.内部类 内部类对象 = new 外部类().new 内部类();
- 实例内部类可以访问外部类的所有成员属性,在外部类中不能直接访问内部类的成员,必须通过内部类的实例访问。
- 多层嵌套中,Outer类包含Inner1,Inner1类又包含Inner2,则在Outer类中不能直接访问Inner2,应通过Inner1的实例访问Inner2类。
- 实例内部类可以访问外部类的静态域
在外部类中创建内部类[外部类.]内部类 内部对象名=new 外部类().new 内部类();
在外部类以外的其他类中访问内部类外部类.内部类 内部类对象 = new 外部类().new 内部类();
class Outer{
//声明第一层内部类
public class Inner1{
//声明第二层内部类
public class Inner2{
//定义内部类的方法
public void add(int x,int y){
System.out.println(x+"+"+y+"="+(x+y));
}
}
}
//外部类中定义普通方法想要访问Inner2类
public void getAdd(){
//外部类通过 Inner1的实例访问Inner2类
Inner1.Inner2 in= new Inner1().new Inner2();
in.add(3,4);
}
}
- 如果在实例内部类Inner与外部类Outer有着同名的成员变量i,则在内部类中,i、this.i、Inner.this.i 都表示Inner类的成员,而Outer.this.i才表示外部类Outer的成员。
class Outer{
//外部类的成员属性
int i=10;
public class Inner{
//内部类的同名成员属性
int i=20;
//内部类的方法
public void getOuter(){
System.out.println("i表示Inner的成员i:"+i);//20
System.out.println(this.i);//20
System.out.println(Inner.this.i);//20
System.out.println(Outer.this.i);//10
}
}
//外部类的同名方法
public void getOuter(){
System.out.println(Outer.this.i);//10
}
}
public class InnerTest3{
public static void main(String[] args){
//通过匿名内部类的对象调用内部类的方法
new Outer().new Inner().getOuter();
//通过匿名外部类的对象调用外部类的方法
new Outer().getOuter();
}
}
(2)静态内部类
静态内部类是声明在外部类中,作为外部类的静态成员的,即使用static 关键字修饰的内部类。与实例内部类只能声明非静态成员不同,静态内部类既可以声明静态成员,也可以声明非静态成员,静态内部类不能访问外部类的非静态成员,只能访问外部类的静态成员(只能通过外部类的实例或对象访问)。静态内部类的对象实例可以独立创建,静态内部类类似于顶层类,只不过被定义在了一个类的内部(静态内部类实例的创建不依赖于外部类)。
在外部类中的静态内部类实例对象创建:
内部类 内部类名=new 内部类();
在其他类中的静态内部类创建对象实例:
外部类.静态内部类 静态内部类对象名 = new 外部类.静态内部类();
class Outer{
//定义外部类属性
private int a=4;
//定义外部类静态属性
private static int b=5;
//定义静态内部类
public static class Inner{
private int x=5;//可定义非静态属性
private static int y=6;//可定义静态属性
//可定义非静态方法
public void add(int x,int y){
int temp=new Outer().a;
//int temp=a;//error
int x=b;//在静态内部类中可直接访问外部类的静态属性
System.out.println(temp+"x"+x+"="+(temp+x));
}
//可定义静态方法
public static void reduce(int x,int y){
System.out.println(x+"-"+y+"="+(x-y));
}
}
//外部类的普通方法
public void getInfo(){
//通过静态内部类的匿名对象调用内部类的方法
new Inner().add(4,3);
//可通过静态内部类的类名直接调用静态内部类的方法
Inner.reduce(4,3);
}
}
public class StaticInner{
public static void main(String[] args){
//创建外部类实例
Outer out=new Outer();
out.getInfo();
//创建内部类实例
Outer.Inner in =new Outer. Inner();
in.add(4,3);
in.reduce(4,3);
}
}
(3)局部内部类
局部内部类是指定义在方法体的内部类,局部内部类仅在该方法有效。
局部内部类不能被外部类以及外部类以外的其他类访问,因此局部内部类是不需要(也不能被)任何访问控制符(public private protected)和static修饰的,
局部内部类不能定义static成员,
局部内部类可以访问外部类的静态成员,
若需调用外部类的非静态成员,可以通过外部类的实例。
局部内部类只能访问所在方法的final类型的参数和变量,若不声明,系统自动将变量或形参转为final类型的变量(JDK8形参变为隐式final声明).
class Outer{
//定义外部类的非静态私有属性
private float f=0.1f;
//定义外部类的静态私有属性
private static int a=7;
//定义外部类的普通方法
public void fun(){
//在普通方法里定义内部类称为局部(方法内部类)
class Inner{
//方法内部类中定义私有属性
private float innerF=4.3f;
public Inner(){
System.out.println(f+"+"+innerF+"="+(f+innerF));
}
}
//在外部类普通方法里创建内部类匿名对象,JVM才能加载方法内部类
new Inner();
}
//外部类中定义静态方法
public static void fun2(){
//静态方法中定义内部类,也不能加sattic 和 访问修饰符
class Inner2{
private int i=2;
public Inner2(){
//在静态方法内部类中直接调用外部类的静态私有属性a
System.out.println(i+"+"+a+"="+(i+a));
//在静态方法内部类中直接通过匿名对象调用外部类的私有属性
float temp= new Outer().f;
System.out.println(i+"+"+temp+"="+(i+temp));
}
}
//在外部类静态方法里创建内部类匿名对象
new Inner2();
}
}
public class PartInner{
public static void main(String[] args){
Outer out = new Outer();
out.fun();
out.fun2();
}
}
(4)匿名内部类(lamdba表达式的前身)
匿名内部类是指在定义时没有名称的类,必须在声明时使用new语句 声明类。匿名内部类是一种特殊的内部类,除了具有普通类的特点以外,还有自己的特点。匿名内部类一般只使用一次。匿名内部类是定义在方法中没有名字的内部类。
匿名内部类语法:
new 类或接口([参数列表]){
.....
};
参数列表表示调用父类构造方法时传入的参数,匿名内部类只在其定义的代码块内使用一次,所以无法为其定义构造方法。匿名内部类总是使用父类的构造方法创建实例,如果匿名内部类实现的是接口,那么匿名内部类的构造方法就是Object().
虽然匿名内部类没有类名,匿名内部类必须扩展一个基类或实现一个接口,但不能明显的使用extends 或 implements关键字。若匿名内部类继承抽象类或实现接口时,还要实现父类及接口中的所有抽象方法。有名称的类若没有显式的指定父类,系统会让其自动继承Object类,但匿名内部类不会自动继承Object类,所有每个匿名内部类都要明确的指出它继承的类或实现的接口。
匿名内部类继承抽象类
abstract class AbstrClass{//定义抽象类
public abstract void getInfo();//声明抽象类
}
class Outer{//定义类(匿名内部类的外部类)
public void print(){
//在print方法中调用show方法,show方法的形参中开辟了抽象类的匿名对象
//实现了抽象类的抽象方法,使之成为匿名内部类
show(new AbstrClass(){
public void getInfo(){ //实现了抽象类中的方法
System.out.println("Java匿名内部类");
}
});//show方法的调用实现了Outer类继承抽象类成为匿名内部类
}
public void show(AbstrClass a){//定义show 方法,形参为AbstrClass类的实例对象
a.getInfo();
}
}
public class AnonymityInner{
public static void main(String[] args){
new Outer().print();//创建外部部类的对象并调用匿名内部类的方法
}
}
匿名内部类实现接口
interface Inter{//定义接口类Inter
public abstract void getInfo();
}
class InterClass{//定义类InnerClass(匿名内部类的外部类)
public void print(){//定义类InnerClass类中的普通方法
show(new Inter(){//调用show方法
//在show方法的参数中通过匿名对象实现接口类的抽象方法,使此类成为匿名内部类
public void getInfo(){
System.out.println("Java匿名内部类,实现接口");
}
});
}
public void show(Inter i){
i.getInfo();
}
}
public class AnonymityInner{
public static void main(String[] args){
new InterClass().print();//创建外部类 InterClass的对象并调用方法
}
}
匿名内部类可以访问外部类的所有成员,但匿名内部类定义在方法之中,只能访问方法中的final类型的参数和局部变量。
abstract class AbstrClass{//定义抽象类
public abstract void getInfo();//声明抽象类
}
class InterClass2{
public void print(int i,final int k){
int x=10;
final int y =20;
show(new Inter(){
public void getInfo(){
System.out.println("print方法的final类型参数:"+k);
System.out.println("print方法的final类型局部变量:"+y);
System.out.println("print方法的非final类型参数:"+i);
System.out.println("print方法的非final类型局部变量:"+x);
}
});
}
public void show(Inter i){
i.getInfo();
}
}
public class AnonymityInner{
public static void main(String[] args){
new InterClass2().print(3,5);
}
}
匿名内部类定义在方法之中,能访问方法中的非final类型的参数和局部变量是因为JDK8会将局部变量和形参变为隐式final声明
匿名内部类允许非静态代码块对成员进行初始化:
class InterClass3{
public void print(){
show(new Inter(){
int x;
{
x=10;
}
public void getInfo(){
System.out.println("x="+x);
}
});
}
public void show(Inter i){
i.getInfo();
}
}
public class AnonymityInner{
public static void main(String[] args){
new InterClass3().print();//创建inner匿名内部类的对象并调用匿名内部类的方法
}
}