内部类(重点,难点,非常非常重要!!!)
警告:此处为水平分水岭,务必理解透彻,即将对面向对象的理解上一个台阶!!!
注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类!!!
1. 内部类的基本介绍
(1)一个类的内部又完整的嵌套了另一个类结构
1. 被嵌套的类称为内部类(inner class)
2. 嵌套其他类的类称为外部类(outer class)。
(2)内部类最大的特点就是可以直接访问私有
属性,并且可以体现类与类之间的包含关系
2. 内部类的分类
定义在外部类的局部位置上
定义在外部类的成员位置上
3. 对一些类概念的说明(关系见如下结构)
class outer_class{
class inner_class{
}
}
class other_outer_class{
}
至此,类的所有内容完善了,形成一个完整的类结构!!!
(1)属性
(2)方法
(3)构造器
(4)代码块
(5)内部类
一、局部内部类
1. 对于局部的理解
(1)既然是局部,说明特点就是在某一个结构中,并且是可以被调用的,调用后将会失效
(2)回顾类的五大结构,引出
2. 对于局部内部类本质的理解
(1)本质是一个类
(2)由于是局部,可以看作是一个局部变量(成员),只是这个变量(成员)是一个类,其特性大同小异
局部内部类的使用细节
(1)说明:局部内部类是定义在外部类的局部位置,并且有类名
(2)可以直接访问外部类的所有成员,包含私有的
(3)不能添加访问修饰符,因为它的地位就是一个局部变量;局部变量是不能使用修饰符的,但是可以使用 final 修饰,因为局部变量也可以使用 final
(4)作用域:仅仅在定义它的方法或代码块中
(5)成员的访问
1. 局部内部类访问外部类的成员
2. 外部类访问局部内部类的成员
访问方式:创建对象,再访问(注意:必须在作用域内
)
(6)外部其他类不能访问局内部类(因为局部内部类地位是一个局部变量)
(7)如果外部类和局部内部类的成员重名时
1. 默认遵循就近原则
2. 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
举例:System.out.println("外部类的 n1=" + 外部类名.this.n1)
理解几个东西
1. 外部类.this
:本质是外部类的对象,哪个对象调用了局部内部类所在的方法或代码块,外部类.this就是哪个对象
2. this.n1
:this 指向的是调用的当前对象,显然就是局部内部类这个对象的属性
代码解析
package inner;
public class main {
public static void main(String[] args) {
System.out.println("你创建了外部类的一个对象,接下来是构造器的初始化\n");
outer_class outer_class = new outer_class();
System.out.println("接下来调用外部类的 test() 方法\n");
outer_class.test();
}
}
class outer_class{
{
class inner_class_code_block{
public void hi(){
System.out.println("调用了 外部类 代码块中的 局部内部类 的 hi() 方法");
}
}
inner_class_code_block inner_class_code_blockblock = new inner_class_code_block();
inner_class_code_blockblock.hi();
}
int n1 = 10;
private final int n2 = 20;
public outer_class(){
System.out.println("调用了 外部类 的 无参构造器\n");
}
public void test(){
System.out.println("进入了外部类的 test() 方法\n");
class inner_class{
final int n1 = 30;
public void test(){
System.out.println("进入了局部内部类的 test() 方法");
System.out.println("1. 访问外部类的成员 n1= " + outer_class.this.n1 + "\n(访问方法:类名.this.n1;注意:当方法名重名的时候,遵循就近原则)" + "\n" );
System.out.println("2. 访问外部类的 私有 成员 n2=" + n2 + "\n");
System.out.println("3. 访问局部内部类的成员 n1=" + n1 + "\n");
}
}
inner_class inner_class = new inner_class();
System.out.println("通过创建对象的方法\n\n一、外部类 调用 局部内部类的 成员 n1=" + inner_class.n1 + "\n");
System.out.println("二、外部类 调用 局部内部类的 test()方法\n");
inner_class.test();
}
}
你创建了外部类的一个对象,接下来是构造器的初始化
调用了 外部类 代码块中的 局部内部类 的 hi() 方法
调用了 外部类 的 无参构造器
接下来调用外部类的 test() 方法
进入了外部类的 test() 方法
通过创建对象的方法
一、外部类 调用 局部内部类的 成员 n1=30
二、外部类 调用 局部内部类的 test()方法
进入了局部内部类的 test() 方法
1. 访问外部类的成员 n1= 10
(访问方法:类名.this.n1;注意:当方法名重名的时候,遵循就近原则)
2. 访问外部类的 私有 成员 n2=20
3. 访问局部内部类的成员 n1=30
二、匿名内部类(重点)
为什么需要匿名内部类?
需求:创建一个类实现接口方法,然后再外部类中调用实现接口的方法
package anonymous;
public class main {
public static void main(String[] args) {
outer_class outer_class = new outer_class();
outer_class.test();
}
}
interface ia{
public void run();
}
class tiger implements ia{
public void run(){
System.out.println("利用接口,实现了run()方法");
}
}
class outer_class{
public void test(){
tiger tiger = new tiger();
System.out.println("在outer_class中调用tiger中的test()方法\n");
tiger.run();
}
}
如果要求该类只使用一次,代码存在的问题
(1)单独创建类会使得代码冗余,代码不够简洁
(2)不利于维护,如果需要修改某个成员,那该类的所有对象都会受到影响,不够灵活
引出—>匿名内部类
1. 基本介绍
(1)本质是类
(2)是内部类
(3)该类没有名字
(4)同时还是一个对象
2. 基本语法
new 类 或 接口(参数列表){
类体
};
3. 匿名内部类的特点
匿名内部类即是一个类的定义,同时它本身也是一个对象,因此从语法上看,他既有定义类的特征,也有创建对象的特征,因此可以调用匿名内部类方法
4. 使用细节
>>提示:匿名内部类和局部内部类都是定义在局部位置上,具有的特性大同小异
(1)可以直接访问外部类的所有成员,包含私有的
(2)不能添加访问修饰符,因为它的地位就是一个局部变量
(3)作用域:仅仅在定义它的方法或代码块中
(4)关于访问
1. 匿名内部类访问外部类成员
2. 外部其他类不能访问匿名内部类(因为匿名内部类地位是一个局部变量)
(5)如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,
(6)如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
举例:System.out.println("外部类的 n1=" + 外部类名.this.n1)
package anonymous;
public class main {
public static void main(String[] args) {
outer_class outer_class = new outer_class();
outer_class.test();
}
}
interface ia{
public void run();
}
class tiger implements ia{
public void run(){
System.out.println("利用接口,实现了run()方法");
}
}
class outer_class{
public void test(){
tiger tiger = new tiger();
System.out.println("在outer_class中调用tiger中的test()方法\n");
tiger.run();
}
}
匿名内部类的使用场景
(1)基于接口的匿名内部类
(2)基于类的匿名内部类
(3)基于抽象类的匿名内部类
(4)类只使用一次,后续不再使用
(5)匿名内部类的应用:类可以作为实参传递
一、代码解析:基于接口的使用方法
package anonymous;
public class main {
public static void main(String[] args) {
outer_class outer_class = new outer_class();
outer_class.test();
}
}
interface ia{
public void run();
}
class outer_class{
public void test(){
ia tiger = new ia() {
@Override
public void run() {
System.out.println("在匿名内部类中,实现了接口方法run()");
}
};
tiger.run();
System.out.println("tiger的运行的运行类型是:" + tiger.getClass());
}
}
在匿名内部类中,实现了接口方法run()
tiger的运行的运行类型是:class anonymous.outer_class$1
说明
(1)匿名内部类没有类名,其实是在底层分配了一个类名(名称为:外部类的类名 + $1(按照使用的个数依次编号))
(2)基于接口的匿名内部类的底层源码分析
class outer_class$1 implements ia{
@Override
public void run() {
System.out.println("在匿名内部类中,实现了接口方法run()");
}
}
(3)当执行new ia()
的时候就表示创建了outer_class$1
对象实例,把地址返回,通过 tiger 来接收,即相当于创建了 ia 类的 tiger 实例,在匿名内部类中重写了 run()方法(实现了接口方法)
(4)对比之前的方法
先创建 tiger 类
实现接口
在 tiger 类中实现接口方法
在外部类中创建 tiger 对象实例,调用 tiger 中的方法
(5)tiger 的运行类型就是匿名内部类
(6)匿名内部类(outer_class$1)使用一次后就不能再使用,但是接收的对象实例 tiger 是可以不断使用的
二、代码解析:基于类的使用方法
package anonymous;
public class main {
public static void main(String[] args) {
outer_class outer_class = new outer_class();
outer_class.test();
}
}
class father{
String name;
public father(){
}
public father(String name){
this.name = name;
}
public void hi(){
}
}
class outer_class{
public void test(){
father father = new father() {
@Override
public void hi(){
System.out.println("匿名内部类重写了father类的hi()方法");
}
};
father.hi();
System.out.println("father的运行类型是:" + father.getClass());
}
}
匿名内部类重写了father类的hi()方法
father的运行类型是:class anonymous.outer_class$1
基于类匿名内部类的底层源码分析
class outer_class$1 extends father{
@Override
public void hi(){
System.out.println("匿名内部类重写了father类的hi()方法");
}
}
三、基于抽象类的使用方法
package anonymous;
public class main {
public static void main(String[] args) {
outer_class outer_class = new outer_class();
outer_class.test();
}
}
abstract class A {
public abstract void hi();
}
class outer_class {
public void test() {
A a = new A() {
@Override
public void hi(){
System.out.println("匿名内部类中重写了抽象类中的hi()方法");
}
};
a.hi();
System.out.println("匿名内部类的运行类型是:" + a.getClass());
}
}
匿名内部类中重写了抽象类中的hi()方法
匿名内部类的运行类型是:class anonymous.outer_class$1
基于抽象类的匿名内部类底层源码分析
class outer_class$1 extends A{
@Overide
public void hi(){
System.out.println("匿名内部类中重写了抽象类中的hi()方法");
}
}
四、匿名内部类的使用实例:作为参数传递
要求:写一个类实现接口,在类中重写接口方法,在主类中调用接口方法
第一种:传统写法
package anonymous;
public class main {
public static void main(String[] args) {
animal animal = new animal();
tool.interface_implement(animal);
}
}
interface A{
void hi();
}
class animal implements A{
@Override
public void hi() {
System.out.println("animal类实现了接口,并重写了接口中的hi()方法");
}
}
class tool{
public static void interface_implement(A interface_member){
interface_member.hi();
}
}
animal类实现了接口,并重写了接口中的hi()方法
问题分析:需要单独创建animal
类实现接口,并重写接口中的方法,过于繁琐,代码冗余
第二种:匿名内部类
package anonymous;
public class main {
public static void main(String[] args) {
tool.interface_implement(new A() {
@Override
public void hi() {
System.out.println("animal类实现了接口,并重写了接口中的hi()方法");
}
});
}
}
interface A{
void hi();
}
class tool{
public static void interface_implement(A interface_member){
interface_member.hi();
}
}
animal类实现了接口,并重写了接口中的hi()方法
代码分析
(1)将匿名内部类作为实参传递,代码简洁
(2)灵活性好:对比传统写法,若需要修改某些内容不会影响到实现了接口A的不同的类
的所有对象实例
总结:匿名内部类的本质可以理解为new 接口或类,在底层就会分配一个类(这个类就是匿名内部类)去实现接口或继承一个类
,返回这个类的对象实例地址,然后可以用一个对象实例接收,实现方法和成员的调用
三、成员内部类
1. 介绍:定义在外部类的成员位置,并且没有 static 修饰
2. 使用细节
(1)可以直接访问外部类的所有成员,包含私有的
(2)可以添加任意访问修饰符(public、protected、默认、private
),因为它的地位就是一个成员
(3)作用域和外部类的其他成员一样,为整个类体
(4)关于访问
1. 成员内部类访问外部类成员
2. 外部类访问成员内部类
(5)如果外部类和内部类的成员重名时
1.内部类访问的话,默认遵循就近原则
2. 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
3. 外部其他类访问成员内部类
理解:首先成员内部类的本质是成员,即可以用通过对象去访问,然而访问的是一个类,需要创建对象实例
方法一:访问成员时创建对象实例
package anonymous;
public class main {
public static void main(String[] args) {
other_class other_class = new other_class();
other_class.hi();
}
}
class outer_class{
class inner_class{
public void hi(){
System.out.println("调用了outer_class中inner_class的hi()方法");
}
}
}
class other_class{
public void hi(){
System.out.println("调用了other_class的hi()方法");
outer_class outer_class = new outer_class();
outer_class.inner_class inner_class = outer_class.new inner_class();
inner_class.hi();
}
}
调用了other_class的hi()方法
调用了outer_class中inner_class的hi()方法
方法二:写一个方法返回对象实例
package anonymous;
public class main {
public static void main(String[] args) {
other_class other_class = new other_class();
other_class.hi();
}
}
class outer_class{
class inner_class{
public void hi(){
System.out.println("调用了outer_class中inner_class的hi()方法");
}
}
public inner_class get_inner_class_instance(){
return new inner_class();
}
}
class other_class{
public void hi(){
System.out.println("调用了other_class的hi()方法");
outer_class outer_class = new outer_class();
outer_class.inner_class inner_class_instance = outer_class.get_inner_class_instance();
inner_class_instance.hi();
}
}
调用了other_class的hi()方法
调用了outer_class中inner_class的hi()方法
四、静态内部类
1. 介绍:静态内部类是定义在外部类的成员位置,并且有 static 修饰
2. 静态内部类的使用细节
提示:静态和普通的区别在于(1)静态只能访问静态的(2)普通的能访问静态和非静态的
(1)可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
(2)可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
(3)作用域:同其他成员,为整个类体
(4)关于访问
1. 静态内部类访问外部类
2. 外部类访问静态内部类
(5)如果外部类和静态内部类的成员重名时
1. 静态内部类访问时,默认遵循就近原则
2.如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
3. 外部其他类访问静态内部类
理解
(1)内部类是静态的,可以通过类名去访问,无需先创建外部类对象
(2)可以直接通过类名访问到静态内部类然后创建对象实例
方法一:类名访问静态内部类,创建对象实例
package anonymous;
public class main {
public static void main(String[] args) {
other_class other_class = new other_class();
other_class.hi();
}
}
class outer_class{
static class inner_class{
public void hi(){
System.out.println("调用了outer_class中inner_class的hi()方法");
}
}
}
class other_class{
public void hi(){
System.out.println("调用了other_class的hi()方法");
outer_class.inner_class inner_class = new outer_class.inner_class();
inner_class.hi();
}
}
调用了other_class的hi()方法
调用了outer_class中inner_class的hi()方法
代码解析:由于是静态的,直接用通过类名访问到静态内部类,之后创建静态内部类对象实例,调用相关的方法即可
方法二:编写一个方法,可以返回静态内部类的对象实例
package anonymous;
public class main {
public static void main(String[] args) {
other_class other_class = new other_class();
other_class.hi();
}
}
class outer_class {
static class inner_class {
public void hi() {
System.out.println("调用了outer_class中inner_class的hi()方法");
}
}
public static outer_class.inner_class get_inner_class_instance() {
return new outer_class.inner_class();
}
}
class other_class {
public void hi() {
System.out.println("调用了other_class的hi()方法");
outer_class.inner_class inner_class_instance = outer_class.get_inner_class_instance();
inner_class_instance.hi();
}
}
调用了other_class的hi()方法
调用了outer_class中inner_class的hi()方法
代码解析:编写一个方法返回静态内部类实例对象,由于是静态的,直接通过外部类的类名访问静态内部类,用一个变量接收返回的静态内部类实例,通过对象调用相关的方法