内部类(必须重点掌握)程序员分层的节点必须记住
基本介绍:
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类【inner class】
,嵌套其他类的类称为外部类【outer class】
。是我们类第五大成员【思考:类的五大成员是哪些?【属性,代码块,构造器,方法,内部类】】
内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
基本语法:
class Outer{ //外部类
class Inner{}//内部类
}
class Other {}//外部其他类
内部类的快速入门:
package JAVA面向对象高级部分.innerClass;
public class InnerClass01 {//其他外部类
public static void main(String[] args) {
}
}
class Outer {//外部类
private int n1 = 168;//属性
{//代码块
System.out.println("我是代码块!!!");
}
public Outer(int n1) {//构造器
this.n1 = n1;
}
class Inner{}//内部类
}
内部类的分类
1.定义在外部类的局部位置上(比如方法或代码块内):
- 局部内部类(有类名)
- 匿名内部类(没有类名,重点最重要)
2.定义在外部类的成员位置上:
- 成员内部类(没有
static
修饰) - 静态内部类(使用
static
修饰)
局部内部类使用LocalInnerClass.java
说明:局部内部类是定义在外部类的局部位置,比如方法中或者代码块,并且有类名。
1.可以直接访问外部类的所有成员,包含私有的
2.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final
修饰,因为局部变量也可以使用final
修饰
3.作用域:仅仅在定义它的方法或代码块中
4.局部内部类 —》访问 —》外部类的成员【访问方式:直接访问】
5.外部类 —》访问 —》局部内部的成员
访问方式:创建对象,再访问(注意:必须在使用域中)
【就是先创建在方法中或代码块中创建局部内部类的对象,再调用内部类的成员,最后再创建外部类对象,调用内部类所在的方法或代码即可】
6.外部其他类 —》不能直接访问 —》局部内部类的【可以通过外部其他类访问外部类,外部类再访问局部内部类】
7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员【成员方法和属性】时,则可以使用【外部类名.this.成员名
】
package JAVA面向对象高级部分.innerClass;
public class LoaclInnerClass {
public static void main(String[] args) {
//5.外部类访问内部类的方法:在内部类所在的方法中或代码块中创建内部类的对象
//对象调用内部类的成员
//6.外部其他类是不能直接访问局部内部类的,只能通过访问外部类,外部类现访问内部类
Outer02 outer02 = new Outer02();
outer02.call();
System.out.println("outer02的哈希值为:"+outer02);
/**
* 返回值:
* num888
* 外部类num=168
* Outer02.this的哈希值为:JAVA面向对象高级部分.innerClass.Outer02@1b6d3586
* 我是外部的show方法
* outer02的哈希值为:JAVA面向对象高级部分.innerClass.Outer02@1b6d3586
*/
}
}
/**
* 局部内部类的使用细节五点:
* 1.局部内部类是可以直接访问外部类的所有成员,包含私有的
* 2.局部内部类是不能添加访问修饰符的,但是只能添加final修饰符,使该内部类不能被继承,和局部属性一样
* 3.作用域是:局部内部类仅仅在定义所在方法内或代码块中
* 4.局部内部类访问外部的成员是可以直接访问的
* 5.外部类访问内部类的方法:在内部类所在的方法中或代码块中创建内部类的对象
* 对象调用内部类的成员
* 6.外部其他类 ---》不能直接访问 ---》局部内部类【因为局部内部类地位就是一个局部变量】
* 7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用【外部类 名.this.成员名】去访问
*/
//外部类
class Outer02 {
private int num = 168;//属性
public void show(){//方法
System.out.println("我是外部的show方法");
}
public void call(){
//2.局部内部类是不能添加访问修饰符的,但是只能添加final修饰符,使该内部类不能被继承
//3.作用域是:局部内部类仅仅在定义所在方法内或代码块中
class Inner02{//局部内部类(本质还是一个类具有类的五大特征)
//7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
//则可以使用【外部类名.this.成员名】去访问
private int num = 888;
public void f1(){
//4.局部内部类访问外部的成员是可以直接访问的
//1.局部内部类是可以直接访问外部类的所有成员,包含私有的
System.out.println("num"+num);//访问是局部内部类的num【其实隐含着内部类的this】输出:888
//要想访问外部类的num可以通过:外部类名.this.属性名
//Outer02.this本质就是外部类的对象,即哪个对象调用num,Outer02.this就是代表那个对象
//这里是代表是:outer02,可以使用哈希值检验
System.out.println("外部类num="+Outer02.this.num);//168
System.out.println("Outer02.this的哈希值为:"+Outer02.this);
show();
}
}
//5.外部类访问内部类的方法:在内部类所在的方法中或代码块中创建内部类的对象
//对象调用内部类的成员
//6.外部其他类是不能直接访问局部内部类的,只能通过访问外部类,外部类再访问内部类
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
匿名内部类的使用
基本介绍:
匿名内部类是定义在外部类的局部位置,比如方法中或代码块中,并且没有类名
- 匿名内部类本质还是类
- 匿名内部类是内部类
- 该类没有名字【但是在系统底层会分配一个名字:
外部类名+$+匿名内部类的序号
】 - 同时还是一个对象
匿名内部类的基本语法:
new 类名或接口名(参数列表){类体};
基于接口的匿名内部类
package JAVA面向对象高级部分.innerClass;
/**
* 匿名内部类的使用
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer03 outer03 = new Outer03();
outer03.method();
}
}
class Outer03 {//外部类
private int n1 = 168;//属性
public void method(){//方法
/**
* 基于接口的匿名内部类
* 1.需求:想使用InterfaceA接口,并创建对象
* 2.传统方式,是写一个类,实现该接口,并创建对象
* 3.需求是:Tiger、Dog等类只上使用一次,后面就不再使用了
* 4.可以使用匿名内部类来简化开发
* 5.tiger的编译类型 ? InterfaceA
* 6.tiger的运行类型 ? 就是匿名内部类 Outer03$1
* 7.jdk底层在创建匿名内部类 Outer03$1,立即马上就创建了 Outer03$1实例,并且把地址返回给tiger
* 8.匿名内部类使用一次,就不能再使用了,但是对象引用tiger可以反复使用
*/
//由于下面的方式是如果很多类,都要实现InterfaceA调用cry()方法,并且只使用一次,这开发太复杂了
//所以可以使用匿名内部类来解决
// InterfaceA tiger = new Tiger();
// tiger.cry();
// InterfaceA dog = new Dog();
// dog.cry();
//使用匿名内部类简化开发
/**
* 我们看看底层 会分配 类名 Outer03$1
* class Outer03$1 implements InterfaceA {
* @Override
* public void cry(){
* System.out.println("老虎叫呵呵!!!");
* }
* }
*/
InterfaceA tiger = new InterfaceA(){
@Override
public void cry() {
System.out.println("老虎叫了呵呵!!!");
}
};
tiger.cry();
//9.匿名内部类的类名是底层分配一个类名:外部类名+$+加上匿名类的序号
System.out.println(tiger.getClass().getName());
}
}
interface InterfaceA {//接口
public void cry();//public可以省略
}
//
//class Tiger implements InterfaceA {
// @Override
// public void cry() {
// System.out.println("老虎叫了呵呵!!!");
// }
//}
//
//class Dog implements InterfaceA {
// @Override
// public void cry() {
// System.out.println("小狗汪汪叫了!!!");
// }
//}
基于类的匿名内部类
package JAVA面向对象高级部分.innerClass;
/**
* 类的匿名内部使用
*/
public class AnonymousInnerClass02 {
public static void main(String[] args) {
}
}
class Outer04 {//外部类
/**
* 演示基于类的匿名内部类
* 分析:
* 1.father编译类型是Father
* 2.father运行类型是Outer04$1
*/
public void method(){
/**
* 3.底层分析:
* class Outer04$1 extends Father {
* @Verride
* public void test(){
* System.out.println("匿名内部类重写了test方法");
* }
* }
*/
//4.同时也直接返回了匿名内部类Outer04$1的对象
//5.注意参数的传递是传给Father类的构造器
Father father = new Father("海康"){
//匿名内部类重写了test方法
public void test(){
System.out.println("匿名内部类重写了test方法");
}
};
father.test();
System.out.println("father对象的运行类型="+father.getClass());//返回:Outer04$1
}
/**
* 基于抽象的匿名内部类,注意必须要实现抽象类中所有抽象方法
*/
Animal animal = new Animal() {
@Override
public void show() {
System.out.println("小猪猪。。。");
}
};
}
class Father {
public Father(String name){//构造器
System.out.println("接收到了名字="+name);
}
public void test(){
System.out.println("Father类中的test()方法!!!");
}
}
abstract class Animal {
public abstract void show();
}
匿名内部类注意事项和使用细节(必须记住8点)
1.匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因为从语法上看,它既有定义类的特征,也有创建对象的特征,对前面的代码分析可以看出这个特点,因此可以调用匿名内部类的方法。
2.可以直接访问外部类的所有成员,包含私有的
3.不能添加访问修饰符,因为它的地位就是一个局部变量
4.作用域:仅仅在定义的方法或代码块中使用
6.匿名内部类可以直接访问外部类的成员【成员方法和属性】
7.外部其他类不能直接访问匿名内部类,要创建外部类对象,再通过外部类访问匿名内部类【因为匿名内部类地位就是一个局部变量】
8.如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用【外部类名.this.成员名【成员方法或属性】
】去访问
package JAVA面向对象高级部分.innerClass;
public class AnonymousInnerClassDetailed {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.method();
System.out.println("main方法的outer05 hashCode值="+outer05);
/**
* 返回值:
* 匿名内部类的cry()方法。。。
* 这是输出的是匿名内部类的num=888
* 使用外部类名.this.成员名访问外部类成员=168
* Outer05.this hashCode=JAVA面向对象高级部分.innerClass.Outer05@1b6d3586
* main方法的outer05 hashCode值=JAVA面向对象高级部分.innerClass.Outer05@1b6d3586
*/
}
}
//外部类
class Outer05 {
//3.可以直接访问外部类的所有成员【成员方法和属性】包含私有的
//4.不能添加访问修饰符,因为它的地位就是一个局部属性
private int num = 168;
public void method(){
//5.作用域:仅仅是在定义的方法或代码块中
//匿名内部的访问方式一:
Person person = new Person(){
//重写cry方法
private int num = 888;
public void cry(){
System.out.println("匿名内部类的cry()方法。。。");
//6.匿名内部类访问外部类的成员【成员方法或属性】是直接访问的
//8.如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则
//如果想访问外部类的成员,则可以使用【外部类名.this.成员名】去访问
System.out.println("这是输出的是匿名内部类的num=" + num);//888
System.out.println("使用外部类名.this.成员名访问外部类成员="+Outer05.this.num);//168
}
};
//7.外部其他类,不能直接访问匿名内部类,可以通过创建外部类的对象,外部类再访问匿名内部类
person.cry();
//Outer05.this就是代表调用cry方法的对象
System.out.println("Outer05.this hashCode="+Outer05.this);
// //匿名内部类访问方式二:
// new Person(){
// //重写show方法
// @Override
// public void show(String name){
// System.out.println(name+"匿名内部类重写show方法。。。");
// }
// }.show("海康");
}
}
//Person类
class Person {
public void cry(){
System.out.println("Person cry()方法。。。");
}
public void show(String name){
System.out.println("Person show()方法。。。");
}
}
匿名内部类的使用场景InnerExercise
1.匿名内部类当做实参直接传递,简洁高效
package JAVA面向对象高级部分.innerClass.inerClassExercise;
public class InnerClassExercise01 {
public static void main(String[] args) {
//传统方法:编程领域【硬编码方式】
//类 --》实现myInterface接口 --》编程领域【硬编码】
method(new Student());
//直接使用匿名内部方式
method(new myInterface() {
@Override
public void show() {
System.out.println("我是一个学生,会干的不只是写代码");
}
});
}
//静态方法:形参是接口
public static void method(myInterface myInterface){
myInterface.show();
}
}
//匿名内部类当做实参直接传递,简洁高效
interface myInterface {
public void show();//public 可以省略
}
//传统方法当做实参
class Student implements myInterface{
@Override
public void show() {
System.out.println("我是一个只会写代码的学生");
}
}
package JAVA面向对象高级部分.innerClass.inerClassExercise;
public class InnerClassExercise02 {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
//3.通过匿名内部类(对象)作为参数,打印:懒猪起床了
cellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了!!!");
}
});
//4.传入另一个匿名内部类(对象),打印:小伙伴上课了
cellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了!!!");
}
});
}
}
/**
* 1.有一个铃声接口Bell,转而有个ring方法
* 2.有一个手机类CellPhone类,具有闹钟功能alarmclock,参数是Bell类型
* 3.测试手机类的闹钟功能,通过匿名内部(对象)作为参数,打印:懒猪起床了
* 4.再传入另一个匿名内部类(对象),打印小伙伴上课了
*/
interface Bell {
void ring();
}
class CellPhone {
public void alarmClock(Bell bell){
bell.ring();//动态绑定
}
}
成员内部类的使用MemberInnerClass
基本介绍:
成员内部类是定义在外部类的成员位置上,并且没有static
修饰的
1.可以直接访问外部类的所有成员,包含私有的
2.可以添加任意的访问修饰符【public protected 默认 private
】,因为它的地位就是一个成员
3.作用域和外部类的其他成员一样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法
4.成员内部类 ----》访问 ----》外部类成员【比如:属性】直接访问
5.外部类 ----》访问成员内部类【访问方式:创建对象,再访问】
6.外部其他类 ----》访问 ----》成员内部类【有三种方式】
7.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用【外部类名.this.成员名
】去访问
package JAVA面向对象高级部分.innerClass.memberInnerClass;
public class MemberInnerClass01 {
public static void main(String[] args) {
// Outer06 outer06 = new Outer06();
// outer06.print();
/**
* 返回值:
* num=168 name=海康
* 我是外部类的show方法
*/
//外部其他类访问成员内部类有三种方式
//方式一:
// Outer06 outer06 = new Outer06();
// Outer06.Inner06 inner06 = outer06.new Inner06();
// inner06.method();
/**
* 返回值:
* num=168 name=海康
* 我是外部类的show方法
*/
/**
* 方式二:
*/
// Outer06 outer06 = new Outer06();
// Outer06.Inner06 inner06 = outer06.getInner06();
// inner06.method();
//方式三:
Outer06.Inner06 inner06 = new Outer06().new Inner06();
inner06.method();
}
}
/**
* 成员内部类
* 1.可以直接访问外部的所有成员,包含私有的
* 2.可以添加任意访问修饰符【public protected 默认 private】,因为它是一个成员
* 3.作用域和外部类的其他成员一样,为整个类体比如下面的print方法,在外部类的成员方法中创建成品同内部类对象,再调用方法
* 4.成员内部类可以直接访问外部类的成员包含私有
* 5.外部类访问成员内部类:访问方式:创建对象,再访问
* 6.外部其他类访问成员内部类有三种方式
* 7.
*/
class Outer06 {//外部类
private int num = 168;
String name = "海康";
public void show(){
System.out.println("我是外部类的show方法");
}
class Inner06 {
//可以直接访问外部类的所有成员
private int num = 888;
public void method(){
System.out.println("这是使用成员内部的num="+num +"\tname=" + name );
//7.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用【外部类名.this.成员名】去访问
System.out.println("成员内部类的num="+Outer06.this.num);
show();
}
}
//访问成员内部类的方式
public void print(){
Inner06 inner06 = new Inner06();
inner06.method();
}
//获取成员内部类的对象
public Inner06 getInner06(){
return new Inner06();
}
}
静态内部类使用
基本介绍:
静态内部类是定义在外部类的成员位置,并且有static
修饰
细节说明:
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加八部访问修饰符【
public protected 默认 private
】,因为它的地位就是一个成员 - 作用域:同其他的成员,为整个类体
- 外部其他类 —》访问 ----》静态内部类
- 如果外部类和静态内部类的成员重名时,静态内部类访问时,默认遵循就近原则,如果想访问外部类的成员,则可以使用【
外部名.成员
】去访问
package JAVA面向对象高级部分.innerClass.staticInnerClass;
public class StaticInnerClass01 {
public static void main(String[] args) {
// Outer8 outer8 = new Outer8();
// outer8.show();
//外部其他类访问静态内部类有三种方法
//方式一:
Outer8.Inner8 inner8 = new Outer8.Inner8();
inner8.say();
//方式二:在外部类中编写一个方法返回静态内部类
Outer8 outer8 = new Outer8();
Outer8.Inner8 inner08 = outer8.getInner08();
inner8.say();
//方式三:在外部类中编写一个静态方法返回静态内部类
Outer8.Inner8 iNer08 = Outer8.getINer08();//此方法不用创建外部类的对象
iNer08.say();
}
}
//外部类
class Outer8 {
private int num = 168;
private static String name = "海康";
static class Inner8 {
//静态成员内部类可以直接访问所有静态成员,包含私有的,不能访问非私有的成员
private static String name = "湛江";
public void say(){
//不能直接访问非静态的成员
// System.out.println(num);//报错
//如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵守就近原则,如果想访问外部类的成员,则可以使用
//【外部类名.成员】去访问
System.out.println("静态成员内部类"+name);
System.out.println("外部类的name"+Outer8.name);//注意不能使用this关键字,因为是静态,对象可能还不存在
}
}
public void show(){
Inner8 inner8 = new Inner8();
inner8.say();
}
//返回内部类对象
public Inner8 getInner08(){
return new Inner8();
}
//静态方法返回内部类对象
public static Inner8 getINer08(){
return new Inner8();
}
}
练习题目:
public class Test{//外部类
public Test(){
Inner s1 = new Inner();
s1.a = 10;
Inner s2 = new Inner();
System.out.println("s2.a");
}
class Inner{
public int a = 5;
}
}
public static void main(String[] args){
Test t = new Test();
Inner r = t.new Inner();
System.out.println("r.a");
}
/**
* 写出输出结果:
* 返回值:
* 5
* 5
*/