内部类概述: 其实就是在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
内部类的定义格式
xxxxxxxxxx
public class 类名{
修饰符 class 类名{
}
}
范例如下
xxxxxxxxxx
public class Outer{
public class Inner{
}
}
内部类的访问特点: 1、内部类可以直接访问外部类的成员,包括私有 2、外部类要访问内部类的成员,必须创建对象
内部类的练习
xxxxxxxxxx
package ch11;
public class a_1_1Outer { //外部类
//定义外部的成员变量
private int num = 10; //被private修饰,则num变成了私有成员变量
//定义内部类
public class Inner {
public void show(){
System.out.println(num); //不报错就证明内部类可以直接访问外部类的成员
}
}
//再定义一个内部类
public void method() {
//show(); //会报错,这个show方法是在上面的Inner外部类里面创建的,method外部类想要访问show方法,就必须创建对象
//上面那行不能直接访问show方法,必须先创建对象,如下
Inner i = new Inner(); //创建Inner类的对象,并调用Inner类里面的show方法。
i.show();//此时就可以访问
}
}
成员内部类
内部类的分类依据:按照内部类在类中定义的位置不同,可以分为如下两种形式
内部类的分类如下 1、在类的成员位置: 成员内部类 2、在类的局部位置: 局部内部类。可以理解为在类的方法里面定义的类
先学习第一种形式--成员内部类
成员内部类,外界如何创建对象使用呢? 格式:
xxxxxxxxxx
外部类名.内部类名 对象名 = 外部类对象.内部类对象
范例:
xxxxxxxxxx
Outer.Inner oi = new Outer().new Inner();
成员内部类的练习
xxxxxxxxxx
package ch11;
public class a_2_1Outer {
//定义外部的成员变量
private int num = 10;
private int num2 = 20;
//在Outer类的成员位置定义一个成员内部类
public class Inner{
public void show(){
System.out.println(num);//内部类是可以直接访问外部类的成员变量
}
}
//上面这样的写法的目的就是把内部类Inner的内容隐藏起来,并不想让外界直接访问,这个写法不常见。常见写法如下
private class Inner2{ //我们会把修饰符改为private。但是,用了private修饰符的话,测试类里的就会报错
public void show2(){
System.out.println(num2);//内部类是可以直接访问外部类的成员变量
}
}
//上面报错的解决方法就是在a_2_1Outer类里面的新写一个method方法,在method方法里面创建Inner2的对象,并调用方法。如下
public void method() {
Inner2 i = new Inner2(); //在外部类里面创建了内部类对象,并调用方法
i.show2();
}
//再去测试类里面需要创建外部类对象,并调用method方法就解决报错了。原理是间接调用show2方法
}
xxxxxxxxxx
package ch11;
public class a_2_2测试 {
public static void main(String[] args) {
//创建内部类对象,并调用方法
//Inner i = new Inner(); //会报错,那么外界如何创建对象使用成员内部类呢?正确写法如下
//格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象
a_2_1Outer.Inner oi = new a_2_1Outer().new Inner();
oi.show(); //控制台输出10
//如果a_2_1Outer类里面的Inner2类加了private修饰符,Inner2类就变成私有,下面就会报错
//a_2_1Outer.Inner2 oi2 = new a_2_1Outer().new Inner2();
//oi.show2();
//上面的报错解决如下
//先去a_2_1Outer类里面的新写一个method方法,在method方法里面创建Inner2的对象,并调用方法
//再回到测试类,创建外部类对象,并调用method方法就解决报错了。如下
a_2_1Outer o = new a_2_1Outer();
o.method(); //间接调用了show2方法
}
}
局部内部类
局部内部类是在方法中定义的类,所以外界是无法直接使用,需要在方法内部创建对象并使用,该类可以直接访问外部类的成员,也可以访问方法内的局部变量
局部内部类的练习
xxxxxxxxxx
package ch11;
public class a_3_1Outer {
//定义成员变量
private int num = 10;
//成员方法
public void method() {
//定义局部变量
int num2 = 20;
//局部内部类,即在方法里面定义类
class Inner{ //外部无法直接访问局部内部类。解决:通过外部类a_3_1Outer对象,调用method方法
//定义方法
public void show(){
//访问外部类的成员变量
System.out.println(num);
System.out.println(num2);
}
}
//直接在测试类里面调用num是无法输出10,会输出空白。解决如下
Inner i = new Inner(); //创建局部内部的Inner类的对象
i.show(); //通过对象调用方法
}
}
xxxxxxxxxx
package ch11;
public class a_3_2测试 {
public static void main(String[] args) {
a_3_1Outer o = new a_3_1Outer(); //通过外部类a_3_1Outer对象,调用method方法,解决外部无法直接访问局部内部类的问题
o.method();
}
}
匿名内部类
匿名内部类本质是匿名的对象,这个对象继承类或者实现了接口 标准点说就是:是一个继承了该类或者实现了接口的子类匿名对象,简单理解就是本质是一个对象,只不过这个对象没有名字
匿名内部类是局部内部类的特殊形式 前提: 存在一个类或者接口,这里的类可以是具体类也可以是抽象类
格式:
xxxxxxxxxx
new 类名或者接口名(){
重写方法;
};
范例:
xxxxxxxxxx
new Inter(){ //使用new说明这个整体是一个对象,只不过这个对象没有名字,所以称为匿名对象
public void show(){
}
};
匿名内部类最后的分号不要漏了
匿名内部类的练习
xxxxxxxxxx
package ch11;
public class a_4_1Outer {
public void method() {
//以接口为例
/*new a_4_3Inter(){
//重写接口里面的show方法
@Override
public void show() {
System.out.println("匿名内部类");
}
};*/
//发现去测试类里面还是没有输出,怎么办。因为本质是对象,所以可以调用方法。所以解决如下
new a_4_3Inter(){
//重写接口里面的show方法
public void show() {
System.out.println("匿名内部类1");
}
}.show(); //原理是自己是对象,所以可以链式编程直接调用自己里面的方法。去测试类里面就有输出了
//思考:如果想要调用2次show方法怎么办。解决方法是再写一遍,如下
new a_4_3Inter(){
//重写接口里面的show方法
public void show() {
System.out.println("匿名内部类2");
}
}.show();
//思考:如果想要调用n次呢。解决方法如下
//原理:既然是对象就有返回值类型,大括号里面的其实是a_4_3Inter()的实现类对象(对于接口而言是实现类对象,如果对于类而言的话就是子类对象)
//那么就可以按照多态的形式赋值给a_4_3Inter()接口,如下
a_4_3Inter i = new a_4_3Inter(){ //大括号指的是这个大括号
//重写接口里面的show方法
public void show() {
System.out.println("匿名内部类-n次");
};
};
i.show(); //编译看左边,执行看右边
i.show(); //重复写这行就表示多次调用,不用向上面两个那样,每调用一次就需要把全部代码全部重写输入一遍
}
}
xxxxxxxxxx
package ch11;
public class a_4_2测试 {
public static void main(String[] args) {
a_4_1Outer o = new a_4_1Outer(); //创建a_4_1Outer类的对象
o.method();
}
}
xxxxxxxxxx
package ch11;
public interface a_4_3Inter {
//抽象方法
void show(); //这行是简写,完整写为public abstract void jump()。因为系统默认会加上public abstract,所以可省略。即默认是抽象的
}
匿名内部类在开发中使用的练习
xxxxxxxxxx
package ch11;
//跳高接口
public interface a_5_1Jumpping {
void jump();
}
x
package ch11;
//接口操作类,里面有一个方法,方法的参数是接口名
public class a_5_2JumppingOperator {
public void method(a_5_1Jumpping j) { //方法的形参是一个接口。新建一个猫类。然后这里就相当于new a_5_4Cat(); 相当于new a_5_5Dog();
j.jump();
}
}
xxxxxxxxxx
package ch11;
public class a_5_3测试 {
public static void main(String[] args) {
//需求: 创建接口操作类的对象,调用method方法
a_5_2JumppingOperator jo = new a_5_2JumppingOperator();
//需要的是接口的实现类对象。所以我们要新建一个类a_5_4Cat。然后再如下操作
a_5_1Jumpping j = new a_5_4Cat(); //等号的左边我们叫左边,等号的右边我们叫右边
jo.method(j); //这里呀传进来的是接口的实现类对象。编译看左边,执行看右边
//上面是猫类实现接口
//-------------------------------------------------------------------------------------------------
//下面是狗类实现接口
a_5_1Jumpping j2 = new a_5_5Dog();
jo.method(j2);
System.out.println("------------------------");
//----------------------------------------------------------------------------------------------------
//每次都要创建一次类的对象,太麻烦。如何使用匿名内部类来优化,如下
jo.method(new a_5_1Jumpping() { //注意匿名内部类的格式
public void jump() {
System.out.println("猫类实现接口类-匿名内部类");
}
});
//再来一次狗类的
jo.method(new a_5_1Jumpping() { //注意匿名内部类的格式
public void jump() {
System.out.println("狗类实现接口类-匿名内部类");
}
});
//优点: 即使把猫类和狗类文件删除也不影响。这里的两个的效果跟最开始新建猫狗类是一样的
}
}
x
package ch11;
//该类去实现接口类
public class a_5_4Cat implements a_5_1Jumpping {
//重写接口的方法
public void jump() {
System.out.println("猫类实现接口类-重写jump()");
}
}
xxxxxxxxxx
package ch11;
//这个狗类也去实现接口类
public class a_5_5Dog implements a_5_1Jumpping {
////重写接口的方法
public void jump() {
System.out.println("狗类实现接口类-重写jump()");
}
}