1.内部类
(学完内部类要求知道内部类的格式和使用)
1.1内部类概述
内部类:就是在一个类中定义一个类。
举例:在一个类A的内部定义一个类B,类B就被称为内部类。
就好比说:笔记本内部有一个CPU,CPU在笔记本的内部,被笔记本包围起来的,CPU就相当于内部类,笔记本就可以看成外部类。
内部类的定义格式:
public class 类名 {
修饰符 class 类名 {
}
}
范例:
public class Outer {
public class Inner {
}
}
内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有。
- 外部类要想访问内部类的成员,必须创建对象。
程序演示:
/*
内部类格式:
public class 类名 {
修饰符 class 类名 {
}
}
内部类访问特点:
内部类可以直接访问外部类的成员,包括私有。
外部类要访问内部类的成员,必须创建对象 。
*/
public class Outer {
//1.在外部类定义私有成员变量测试访问特点
private int num = 10;
public class Inner {
//2.在类里无法直接写输出语句要有main方法
//3.所以先写方法
public void show() {
//4.发现在内部类的里面可以直接访问外部内的成员,包括私有
System.out.println(num);
}
}
//5.在外部内中访问内部类的show方法首先要定义一个方法
public void method() {
//6.因为show方法在内部类中无法直接访问,需要创建对象调用方法
Inner i = new Inner();
i.show();
}
}
1.2成员内部类
内部类的分类:
按照内部类在类中定义的位置不同,可以分为如下两种形式:
- 在类的成员位置:成员内部类。
- 在类的局部位置:局部内部类。
成员内部类,外界如何创建对象使用呢?
- 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
外部类名.内部类名:限制了我们这个内部类。
对象名:也就是内部类的对象会有一个名字。
外部类对象.内部类对象:也就是这里要的是一个外部类对象去访问它下面内部类对象,而内部类对象也是要造出来的。
那么这个成员内部类格式在代码中应该怎么写呢?
- 范例:Outer.Inner.oi = new Outer().new Inner();
这个Outer.Inner限定了一下这个Inner是Outer下面的。
然后oi就是一个对象然后等于外部类对象就是new Outer()
而内部类对象就是new Inner()。最终这个代表的是内部类的对象。
程序演示成员内部类:
public class Outer {
//1.Outer的成员变量
private int num = 10;
//2.Outer类的成员位置,定义成员内部类
// public class Inner{
// public void show(){
// //3.+-
// 在内部类中的show方法里访问Outer的成员变量num
// System.out.println(num);
// }
// /*9.把这样一个内隐藏在外部类里面,其实就是为了把它的内容
// 隐藏起来,并不想让外界直接看到,也就是说我是不会让你直接
// 访问的。所以说这样写法是不常见的。*/
// }
/*10.一般来说这个写法是这样的,先把上面的内部类注释掉
一般来说这个内部类这个修饰符不会使用public,而是用private
*/
private class Inner {
//14.虽然在外界无法直接访问这个私有内容,但是在外部类是可以直接访问的.
public void show(){
System.out.println(num);
}
}
//15.所以就要在外部方法中来写
public void method(){
//16.在method方法里面创建了Inner内部类的对象,并调用了它的方法
Inner i = new Inner();
i.show();
}
}
class InnerDemo {
public static void main(String[] args) {
//5.创建内部类对象,调用方法
// Inner i = new Inner();//报错
/*6.因为这个Inner是放到Outer里来定义的所以不能直接访问
我们来创建Outer是可以的但是我们直接创建Inner的对象是不可以的。
但是这样写又是可以的,到底怎样写呢?回到笔记中看成员内部类外界如何创建对象。
*/
//7.创建内部类对象
// Outer.Inner oi = new Outer().new Inner();
// //8.然后通过创建出来的内部类对象调用方法
// oi.show();//10
/*12.经过11的改写上面这个创建内部对象就无效了。
因为上面 这个创建对象针对的不是私有的,私有的不能这样来写。
所以现在这样也做不到了,把上面的创建对象和调用方法也注释掉。
*/
//13.我们到底该怎么用呢?→14.
/*17.所以在测试类里面只需要创建外部类的对象调用method方法就可以了.
这样就间接的调用了这个方法,也就是说我们不能直接调用Inner的方法,但是我们
可以通过method方法来调用Inner它里面的show方法
*/
Outer o = new Outer();
o.method();//10
}
}
1.3局部内部类
先到程序中演示再小结:
public class Outer {
//1.定义成员变量
private int num = 20;
//2.成员方法
public void method(){
/*10.调用method方法进来,进来之后仅仅是一个
类的定义,没有任何地方体现了show方法的调用,所以
在控制台就不可能输出这个num的值,这个时候应该怎么使用呢?
接着看11.
*/
//15.14说完后到外部method方法内来定义一个局部变量
int num2 = 10;
//3.在外部方法里面写一个局部内部类
class Inner {
//4.局部内部类里面也来一个方法show
public void show(){
//5.在局部内部类的方法里面访问外部的成员变量num
System.out.println(num);
/*6.局部内部类在外界也是无法直接访问的,
也需要通过外部类的对象调用method方法,来间接的访问.
所以要来写一个测试类.
*/
//16.然后在内部类中的show方法中去访问这个num2的值
System.out.println(num2);//也是可以的
}
}
//11.在method方法里面创建Inner的对象
Inner i = new Inner();
//12.然后对象调方法
i.show();
}
}
/*
测试类
*/
class OuterDemo {
public static void main(String[] args) {
//7.直接创建外部内对象
Outer o = new Outer();
//8.直接通过外部类对象调用method方法执行
o.method();
/*9.这里执行是没有内容的,为什么呢?
可以回到Outer类中查看method方法其实都没有调用show方法,
既然show方法都没有被调用,当然这里调用方法是输出不了内容的
回到method方法中继续讲解.
*/
//13.然后经过11和12的操作之后执行就可以在控制台得到num的值了
/*14.这样也就发现了局部内部类它的方法调用也是间接调用.
通过外部内调用它的成员方法,在方法内部来创建局部内部类对象来调方法.
*/
//17.经过15和16的操作后这次执行会在控制台输出两个两个数据的输出
}
}
局部内部类小结:
局部内部类是在方法发中定义的类,所以外界是无法直接使用的,需要在方法内部创建对象并使用。
该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
1.4匿名内部类
匿名内部类它是局部内部类的一种特殊形式所以,它也归属于局部内部类。
那么它特殊在那里呢?
它又这么一个前提:存在一个类或者接口,这里的类可以是具体类也可以是抽象类。(如果是类的话,抽象类居多)
特别之处体现在哪里呢? 就体现在格式上面
我们的一个类都是class开头,但是我们的匿名内部类居然是用new关键字开头的。
格式
new 类名或者接口名(){
重写方法;
};
根据这个格式我们给出一个范例:
new Inter() {
public void show() {
}
}
new Inter; new后面是一个类或者是一个接口,然后有一个小括号然后大括号,打括号里面重写了一个show方法.
重写方法说明它继承了前面这个类或者实现了一个接口,而new说明这个整体是个对象,只不过这个对象没有名字,所以我们叫匿名的对象,其实匿名内部类的本质是一个匿名的对象,这个对象它继承了这个类或者实现了这个接口.
本质:是一个继承了该类或者实现了该接口的子类匿名对象.
然后到程序中演示一下:
/*
前提:
存在一个类或者接口
这里的类可以是具体类也可以是抽象类
格式:
new类名或者接口名(){
重写方法;
};
本质是什么呢?
是一个继承了该类或者实现了该接口的子类匿名对象
*/
public class Outer {
public void method(){
/*2.写一个匿名内部类,匿名内部类归属局部内部类所以要写在外部方法里面
并且它的本质是继承了类或者实现了接口的子类匿名对象,所以要新建一个类或者接口
*/
//5.根据格式写匿名内部类
/*8.从method方法进来之后我们写了这样一种格式,虽然写了并没有去调用这个show方法呀
这么调用show方法呢?
*/
// new Inter(){
// @Override
// public void show() {
// System.out.println("匿名内部类");
// }
//6.这个匿名内部类的本质是一个对象,只不过没有名字而已
// };
/*10.再来看一下它的本质,既然它的本质就是个对象,只要是个对象就一定可以调方法,把上面
匿名对象注释掉,重新改写
*/
// new Inter(){
// @Override
// public void show() {
// System.out.println("匿名内部类");
// }
/*11.这样写就是使用对象调方法,使用的是new Inter对象调用show方法,这个show方法
它重写了这个接口里面的,所以接口里面输出的是匿名内部类这个内容.
*/
// }.show();
//13.假如我想调用两次怎么办呢?直接把这个匿名内部类再写一个就可以了:
// new Inter() {
// @Override
// public void show() {
// System.out.println("匿名内部类");
// }
// }.show();
/*15.如果想更多次的应该怎么办呢?一次一次的复制就太麻烦了.既然说了他是一个对象,
我们在来说这个对象应该有一个返回值类型,那么它是什么类型呢?你看它new的是int,那么
它在这个里面还重写了show方法,所以我认为把它看成一个Inter的实现类对象,如果它是Inter
的实现类对象的话,它应该就可以按照多态的形式赋值给Inter这个接口,把上面的匿名内部类都注释掉,
然后开始改写
*/
/*16.创建Inter接口对象,并把这个匿名内部类赋值给这个接口,
然后就会自动弹出重写Inter接口抽象方法的代码.将来new Inter这个对象一定是接口Inter j的
实现类接口对象. 如果new后面是个类呢?拿new 类就是这个类的子类对象.
现在我把这个实现类对象new Inter(){...}赋值给这个接口,肯定是没有问题的,然后就是多态的形式.
*/
Inter i = new Inter() {
@Override
public void show() {
System.out.println("匿名内部类");
}
};
//17.然后出来直接通过i调用show方法
i.show();
/*18.此时编译看左边,Inter里面有show方法,
执行看右边,右边就是new Inter(){...}这个整体,它重写了show方法,所以会在控制台输出匿名内部类
如果我要多次调用的话直接用i多次调用就可以了.
*/
}
}
/*
3.创建一个接口
*/
interface Inter {
//4.在接口里面给一个抽象方法
void show();
}
/*
测试类
*/
class OuterDemo {
public static void main(String[] args) {
//1.创建Outer的对象并调用了了method方法
Outer o = new Outer();
//2.现在调用里面什么都没有,所以不演示了
o.method();
//7.method里面有内容了执行为什么还没有内容呢?
//12.经过10和11的改写现在执行就可以得到输出结果 匿名内部类
//14.经过13的操作后,这样执行就得到两次show方法的内容
}
}
匿名内部类在开发中的使用
先看需求:
这里有一个跳高的接口,在接口中有一个跳高的抽象方法
然后还有一个接口的操作类,里面有一个方法方法的参数是接口名(前面说过了如果一个方法的形参是接口名它其实要得是该接口的实现类对象),变量是j并在方法中使用变量j调用jump方法.
最后这时我们的测试类,在测试类中有一个main方法,在main方法里面有一个需求:创建接口操作类的对象,调用method方法.
代码里做一下:
/*
跳高接口
*/
public interface Jumpping {
void jump();
}
class Cat implements Jumpping{
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
}
/*
8.狗类实现接口
*/
class Dog implements Jumpping {
//9.重写方法
@Override
public void jump() {
System.out.println("狗可以跳高了");
}
}
/*
接口操作类,里面有一个方法,方法的参数是接口名
*/
class JumppingOperator {
public void method(Jumpping j){//4.Jumpping j = new Cat(); 11.传递过来new Dog();
//6.然后编译看左边,执行看右边
j.jump();
}
}
class JumppingDemo {
public static void main(String[] args) {
//需求:创建接口操作类的对象,调用method方法
//1.创建操作类对象
JumppingOperator jo = new JumppingOperator();
//2.新建一个Cat类实现接口,并创建对象
Cat c = new Cat();
//3.使用jo调用对象,把c传递过去,此处c就等于new Cat();
jo.method(c);//猫可以跳高了
//7.假如我不想让猫跳高了,我想让狗跳高,就要新建一个类
//10.然后创建对象并调用方法
Dog d = new Dog();
jo.method(d);//狗可以跳高了
System.out.println("----------");
/*12.假设有还跟多动物但是我就用一次,每用一次就要去建一个类
然后来创建对象来传递参数.关键是还要再写一个类,太麻烦了,所以这个操作我们
要采用我们学习的匿名内部类来改进它.
*/
/*13.jo.method()方法它要的是一个接口,其实要得是该接口的实现类对象,而
我们说了匿名内部类的本质就是一个对象,并且是实现了该接口的一个对象,并且是
匿名的,也就是说我要得对象没必要再来一个类,我直接按照内部类的格式就可以了*/
/*14.直接使用jo.method把匿名内部类传递过去(因为匿名内部类是一个对象,
并且是实现了接口的对象)*/
jo.method(new Jumpping() {
@Override
public void jump() {
//15.在匿名内部类中改写方法
System.out.println("猪学会了跳高");
}
});//执行得到猪学会了跳高
jo.method(new Jumpping() {
@Override
public void jump() {
System.out.println("人也学会了跳高");
}
});//执行得到人也学会了跳高
jo.method(new Jumpping() {
@Override
public void jump() {
System.out.println("猫学会跳高了");
}
});//16即使我把猫这个类删掉都可以,因为这个跟猫没有关系
//17.所以说我们采用后面这种形式不需要写这么多类出来
}
}