内部类总结
一个类的内部又完整的嵌套了另一个类的结构,被嵌套的类称为内部类,嵌套它的类称为外部类
类的五大成员:
基本语法
class Outer{
class Inner{
}
}
class Other
内部类的分类:
局部内部类的使用
定义在方法中或者代码块里,作用域在方法体或者代码块中,本质仍是一个类
-
可以直接访问外部类的所有成员,包括私有成员
-
不能添加访问修饰符,因为它的地位与一个局部变量相同,局部变量是不可以用修饰符的。但是与局部变量相同,可以用final 修饰
-
作用域:仅仅在定义它的方法或者代码块中
-
局部内部类访问外部类的成员可以直接访问,而外部类访问局部内部类的成员需要创建对象再访问,且必须在作用域内
使用示例:
package com.innerclass_;
public class Test01 {
public static void main(String[] args) {
Outer outer = new Outer(101);
outer.m1();
outer.inner01.speak();
}
}
class Outer{
class Inner01{//内部类,在Outer类的内部
public void speak(){
System.out.println("Inner01中的n1:" + n1);
}
}
Inner01 inner01 = new Inner01();
//作用域仅在定义它的方法中
private int n1 = 100;
public void m1(){
System.out.println("方法m1");
//可以用final修饰
final class Inner02{//局部内部类
//可以直接访问外部类的所有成员
public void f1(){
System.out.println("Inner02中的n1:"+n1);
System.out.print("Inner02中的:");
m2();
}
}
Inner02 inner02 = new Inner02();
//在方法中使用内部类需要创建Inner02对象,然后调用内部类方法即可
inner02.f1();
}
private void m2(){
System.out.println("方法m2");
}
{
System.out.println("Outer代码块");
}
public Outer(int n1) {
System.out.println("Outer构造器");
setN1(n1);
}
public int getN1() {
return n1;
}
public void setN1(int n1) {
this.n1 = n1;
}
}
//输出:
Outer代码块
Outer构造器
方法m1
Inner02中的n1:101
Inner02中的:方法m2
Inner01中的n1:101
-
外部其他类不能访问局部内部类(因为局部内部类是一个局部变量)
-
如果外部类和局部内部类的成员重名,默认遵守就近原则,如果想访问外部类的成员,可以使用外部类名.this.成员去访问。
示例:
public class Test01 { public static void main(String[] args) { Outer outer = new Outer(101); outer.inner01.getName(); outer.inner01.getInner01(); outer.inner01.sayHi(); outer.inner01.getOuterSayHi(); } } class Outer{ class Inner01{//内部类,在Outer类的内部 private String name = "yll"; public void speak(){ System.out.println("Inner01中的n1:" + n1); } public void getName(){ System.out.println("Inner01中的成员name:"+ name); //Outer本质是外部类的对象,即哪个对象调用了m1,Outer.this就是哪个对象 System.out.println("Inner02中的成员name:"+Outer.this.name); } public void getInner01(){ System.out.println(inner01); } public void sayHi(){ System.out.println("Inner01 say hi."); } public void getOuterSayHi(){ System.out.print("Inner01中访问sayHi:"); Outer.this.sayHi(); } } Inner01 inner01 = new Inner01(); //作用域仅在定义它的方法中 //Inner02 inner02 = new Inner02(); private String name = "mxy"; public void sayHi(){ System.out.println("Outer say hi"); } } //输出: Outer代码块 Outer构造器 Inner01中的成员name:yll Inner02中的成员name:mxy com.innerclass_.Outer$Inner01@1b6d3586 Inner01 say hi. Inner01中访问sayHi:Outer say hi
匿名内部类
匿名内部类是定义在外部类的局部位置中的类,且没有类名
new 类或接口(参数列表){
类体
};
匿名内部类解决的问题:
//基于接口的匿名内部类
//需求:想使用接口A,并创建对象,且此对象只使用一次,以后不再使用
//传统方式:写一个类实现接口,并创建对象使用
//不足之处:
// 假如需要用许多调用不同的cry方法的对象话需要创建许多类,不方便
// A a = new Tiger();
// a.cry();
// a = new Cat();
// a.cry();
//由此引出匿名类
例子(基于接口的匿名内部类):
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer2 outer2 = new Outer2();
outer2.method();
}
}
class Outer2{
private int n1 = 10;
public void method(){
//基于接口的匿名内部类
//需求:想使用接口A,并创建对象,且此对象只使用一次,以后不再使用
//传统方式:写一个类实现接口,并创建对象使用
//不足之处:
// 假如需要用许多调用不同的cry方法的对象话需要创建许多类,不方便
// A a = new Tiger();
// a.cry();
// a = new Cat();
// a.cry();
//由此引出匿名类,使用匿名类简化开发
A tiger = new A(){
@Override
public void cry() {
System.out.println("Tiger cry~");
}
};
//tiger的运行类型是什么?就是匿名内部类
//底层:
// class XXXX implements A{
//
// @Override
// public void cry() {
// System.out.println("Tiger cry~");
// }
// }
//可以根据此方法来查看类名:(类名为外部类加$加序号)
System.out.println("tiger的运行类型="+tiger.getClass());
//输出为tiger的运行类型=class com.anonymous_.Outer2$1
//new 表示在底层创建了匿名类Outer2$1后立马就创建了实例,并把地址返回给tiger
//匿名类不能重复使用,但是对象可以
tiger.cry();
}
}
interface A{
public void cry();
}
class Tiger implements A{
@Override
public void cry() {
System.out.println("Tiger Cry~");
}
}
class Cat implements A{
@Override
public void cry() {
System.out.println("Cat meow meow~");
}
}
基于类的匿名内部类:
class Outer3{
public void sayHi(){
//基于类的匿名内部类
//father的编译类型:Father
//运行类型:Outer2$2
//不带大括号运行类型就是Father
//注意("mxy")会传递给Father构造器,但是不能重写Father构造器
Father father = new Father("mxy"){
@Override
public void speak() {
System.out.println("内部匿名类重写了speak方法");
}
};
//输出:class com.anonymous_.Outer2$1
System.out.println("father对象的运行类型:"+ father.getClass());
}
}
class Father{
private String name;
public void speak(){
System.out.println("类Father speak");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Father(String name) {
setName(name);
}
}
基于抽象类的匿名内部类:
抽象类的匿名类必须实现,原因与抽象类的子类必须实现相同
public class AnonymousTest {
public static void main(String[] args) {
Outer.play();
}
}
class Outer{
public static void play(){
Animal animal = new Animal() {
//必须实现
@Override
public void eat() {
System.out.println("小狗吃骨头...");
}
};
animal.eat();
}
}
abstract class Animal{
public abstract void eat();
}
匿名内部类细节:
-
匿名内部类即是类的定义,其本身也是一个对象
-
可以直接调用
class Outer{ public static void play(){ new Animal() { @Override public void eat() { System.out.println("小猫吃鱼..."); } }.eat(); //只调用一次eat方法可以这么写 } }
-
可以访问外部类的所有成员,包括私有的
-
不能添加访问修饰符,因为其地位就是局部变量
-
外部其他类不能访问匿名内部类
-
若外部类和匿名内部类的成员重名,匿名内部类访问时遵循就近原则,若非要访问外部类成员,可以用 外部类名.this.成员去访问
匿名内部类的实践:
-
当作实参直接传递,简洁高效
public class Use { public static void main(String[] args) { show(new AA(){ @Override public void cry() { System.out.println("I'm crying..."); } }); } public static void show(AA a){ a.cry(); } } //若不使用匿名内部类,需要对接口进行实现,然后再new对象传参,很麻烦(硬编码) interface AA{ public void cry(); }
成员内部类
成员内部类定义在外部类的成员位置,并且无static修饰
注意:
-
可以访问外部类所有成员,包括私有的
public class Test1 { public static void main(String[] args) { Outer outer = new Outer(); outer.t1(); } } class Outer{ private int n1 = 10; class Inner{ public void say(){ System.out.println("Outer的n1:"+n1); } } public void t1(){ Inner inner = new Inner(); inner.say(); } }
-
可以添加任意的访问修饰符(public protected 默认 private),因为其地位就是一个成员
-
作用域:
-
与外部类其他成员一致,为整个类体
-
成员内部类访问外部类可以直接访问
-
外部类访问成员内部类需要先创建再访问
-
外部其他类访问成员内部类(可以的),访问方式如下:
-
创建外部对象后new来访问
public class Test1 { public static void main(String[] args) { Outer outer = new Outer(); //1、 Outer.Inner inner = outer.new Inner(); //new Inner();Inner是成员 因此需要先创建Outer后再创建 } }
-
在外部类中创建一个返回new内部类的方法,通过调用外部类的方法来访问
public class Test1 { public static void main(String[] args) { Outer outer = new Outer(); outer.t1(); //2、 Outer.Inner inner1 = outer.getInner(); } }
-
-
-
如果外部类和内部类的成员重名时,内部类访问会遵顼就近原则, 因此若想访问外部类的成员,需要(外部类名.this.成员)去访问
静态内部类
定义在外部类的成员位置,有static修饰
注意:
-
可以直接访问外部类的所有静态成员,包括私有的,但是不能访问非静态成员
-
可以添加任意访问修饰符,因为其地位就是一个成员
-
作用域:同其他的成员一样,为整个类体
public class Test1 { public static void main(String[] args) { Outer outer = new Outer(); outer.useInner(); } } class Outer{ private static int n1 = 1000; private double nt = 99.99; public static class Inner{ public void say(){ System.out.println("Outer类中的n1:"+n1); //System.out.println("Outer类中的nt"+ nt); //不可访问非静态成员 } } public void useInner(){ Inner inner = new Inner(); inner.say(); } }
-
访问:
-
内部类可以访问外部类所有的静态成员,但是不能访问非静态成员
-
外部类访问内部类可以通过类名.静态成员来访问,或创建内部类访问其非静态成员
-
外部其他类访问内部类的方法:
-
1、由于是静态内部类,可以直接通过类名访问(在访问权限内)
public class Test1 { public static void main(String[] args) { //外部其他类使用Inner不需要新建Outer Outer.Inner inner = new Outer.Inner(); } }
-
2、编写一个方法,返回静态内部类(尽量写为静态方法)
public class Test1 { public static void main(String[] args) { //静态方法返回Inner Outer.Inner inner1 = Outer.getInner(); } }
-
-
-
重名:依然遵守就近原则,若要访问外部类,直接用外部类名.成员去访问(只能是静态成员)