面向对象(17):内部类及其案例
面向对象(17):内部类及其案例
一、内部类的介绍
1、内部类的概述
把类定义在其他类的内部,我们称之为内部类
举例:在类A中定义了一个类B,类B就是内部类
2、内部类的特点
(1)内部类可以访问外部类的成员,包括私有和静态的外部类成员
(内部类如果是静态的,就不能访问外部类私有的,但是可以访问静态的成员)
案例:
class Outer{
private int num = 10;
class Inner{
public void show(){
System.out.println(num);
}
}
}
(2)外部类要想访问内部类的成员,就必须要创建内部类的对象
案例:
class Outer{
private int num = 10;
class Inner{
public void show(){
System.out.println(num);
}
}
//在外部类中创建一个方法,在方法中创建内部类对象,通过内部类对象来访问内部类成员
public void show2(){
//创建内部类对象
Inner inner = new Inner();
inner.show();
}
}
3、内部类位置
根据内部类定义的位置不同,可以分为两种类型:
(1)成员的位置上(成员内部类)
(2)局部的位置上(局部内部类),定义在方法内的
案例:
class Outer2{
//成员内部类
class Inner2{
}
public void fun(){
//局部内部类
class Inner3{
}
}
}
4、成员内部类的修饰符
成员内部类常见的修饰符为:private、static
private:为了保证数据安全性
static:为了方便访问数据
静态修饰的特点:
内部类如果是静态修饰的,只能访问外部类静态的成员
案例:
class Outer4{
private int num = 10;
private static int num2 = 30;
static class Inner4{
public static void show2(){
//System.out.println(num);静态的内部类不能访问外部私有的,会报错
System.out.println(num2)//静态的内部类可以访问外部类静态的成员
}
}
二、成员内部类
1、如果内部类是非静态的,在测试类中该如何访问内部类中的成员?
格式(1)外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
= 外部类对象.内部类对象;
(2)通过对象名调取
2、如果内部类是静态的,在测试类中如何访问内部类中的成员呢?
如果内部类是静态的,内部类成员是非静态的 ,测试类中需要创建对象来调取内部类成员
外部类名.内部类名 对象名 = new 外部类类名.内部类类名();
对象名.成员名;//调取静态内部类的非静态成员变量
对象名.成员名();//调取静态内部类的非静态成员方法
如果内部类是静态的,内部类成员也是静态的 ,测试类中可以通过类名直接调用内部类成员
外部类名.内部类名.成员名;//调取静态内部类的静态成员变量
外部类名.内部类名.成员名();//调取静态内部类的静态成员方法
3、案例:
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(?);
System.out.println(??);
System.out.println(???);
}
}
}
需求:在控制分别输出:30,20,10
答:
//外部类
class Outer5 {
public int num = 10;
//内部类
class Inner5 {
//成员内部类
public int num = 20;
public void show() {
int num = 30;
System.out.println(num);//这里的num代表方法里的局部变量30
System.out.println(this.num);//this表示这里的num代表当前类的成员变量
//Inner5与Outer5不是继承关系,不能使用super关键字
//System.out.println(super.num);
System.out.println(Outer5.this.num);//表示当前外部类下的成员变量10
//或者 System.out.println(new Outer5().num);通过外部类对象调取外部类成员变量
}
}
}
public class InnerClassDemo5 {
public static void main(String[] args) {
//内部类是非静态的,成员也非静态的
Outer5.Inner5 oi5 = new Outer5().new Inner5();
oi5.show();
}
}
执行结果如下:
30
20
10
Process finished with exit code 0
三、局部内部类
1、位置:定义在方法内的类称之为局部内部类
2、局部内部类的特点
特点(1)可以直接访问外部类的所有成员
案例:
class Outer6{
private int num = 10;
public void fun(){
int num2 = 100;
//局部内部类
class Inner6{
int num3 = 200;
//局部内部类中的方法
public void show(){
System.out.println(num);//外部类的成员变量
System.out.println(num2);//外部类方法中的成员变量
System.out.println(num3);//局部内部类中的成员变量
}
}
如果想要实现上面局部内部类的show方法,该怎么办?
①需要在外部类成员方法内、局部内部类外,创建局部类对象
②在测试类中中创建外部类对象,调取外部类成员方法
class Outer6{
private int num = 10;
//外部类的成员方法
public void fun(){
int num2 = 100;
//局部内部类
class Inner6{
int num3 = 200;
//局部内部类中的方法
public void show(){
System.out.println(num);//外部类的成员变量
System.out.println(num2);//外部类方法中的成员变量
System.out.println(num3);//局部内部类中的成员变量
}
}
//需要在外部类的成员方法中创建内部类对象
Inner6 inner6 = new Inner6();
//调取内部类的show方法
inner6.show();
}
}
//测试类
public class InnerClassDemo6 {
public static void main(String[] args) {
//创建外部类对象
Outer6 outer6 = new Outer6();
//调取外部类成员方法
outer6.fun();
}
}
执行结果如下:
10
100
200
Process finished with exit code 0
由上得出局部内部类的第二个特点:
特点(2)可以在外部类中的局部范围中创建对象,通过对象调用内部类中的方法,来使用内部类的局部功能
3、局部内部类访问局部变量的注意事项
通过反编译工具查看后发现, 局部内部类存在的方法中定义的局部变量自动加上了final,不能进行二次赋值
特点:jdk1.8之后会自动添加final关键字
四、匿名内部类
1、匿名内部类的含义
匿名内部类是内部类的一个简化写法
2、存在匿名内部类的前提
要存在一个类或者是一个接口,类可以是具体的类也可以是抽象类
3、定义格式
new 类名或者接口名(){重写方法;}
本质上:
是一个继承了这个类或者实现了这个接口的子类匿名对象
案例:
/创建一个接口
interface Inner{
//定义两个抽象方法
public abstract void show1();
public abstract void show2();
}
class Outer{
//创建一个方法
public void fun(){
//匿名内部类
new Inner(){
//重写方法
@Override
public void show1() {
System.out.println("这是show1方法");
}
@Override
public void show2() {
System.out.println("这是show2方法");
}
}.show1();//new Inner(){}整体相对于new了一个接口的对象
//可以直接调用接口中的方法
}
}
public class InnerClassDEmo11 {
public static void main(String[] args) {
//想要调用fun方法,必须先创建外部类对象
Outer outer = new Outer();
outer.fun();
}
}
执行结果如下:
这是show1方法
Process finished with exit code 0
//如果想要再调取show2方法,必须再创建一个匿名内部类,因为匿名对象只能使用一次
想一想,如果接口中的方法很多的时候,每次调用一个方法,都需要new一下,要写的内容都重复
了,怎么改进?
使用多态的形式,这里叫做接口多态,将匿名内部类用接口来接收一下
/创建一个接口
interface Inner{
//定义两个抽象方法
public abstract void show1();
public abstract void show2();
}
class Outer{
//创建一个方法
public void fun(){
//接口多态:用接口来接收一下匿名内部类
Inter i = new Inner(){
//重写方法
@Override
public void show1() {
System.out.println("这是show1方法");
}
@Override
public void show2() {
System.out.println("这是show2方法");
}
};
i.show1();//在fun方法中直接调取show1()
i.show2();//在fun方法中直接调取show2()
}
}
public class InnerClassDEmo11 {
public static void main(String[] args) {
//想要调用fun方法,必须先创建外部类对象
Outer outer = new Outer();
outer.fun();
}
}
执行结果如下:
这是show1方法
这是show2方法
Process finished with exit code 0
学习了匿名内部类,接口的写法又多了一个
之前:
//接口
interface A{
b();
c();
}
//创建一个类来实现接口
class B implements A{
b(){..}//重写方法
c(){..}//重写方法
}
//在测试类中使用接口多态创建对象
A a = new B();
现在:
//接口
interface A{
b();
c();
}
//在外部类中的一个方法内
A a = new A(){
b(){..}//重写方法
c(){..}//重写方法
};
//测试类中
创建外部类对象,然后调取外部类的方法,就相当于实现了接口
五、匿名内部类在开发中的使用案例
实现接口的方法一:接口传参
//在开发中,会有人提供一个接口给我们
interface Person{
public abstract void study();
}
//定义一个类
class PersonDemo{
//创建一个方法,接口作为形参传入进去
//当接口作为方法的参数的时候,实际上需要的是实现该接口类的对象
public void fun(Person p){
p.study();
}
}
//定义一个类,来实现该接口
//当接口作为方法的参数的时候,需要的就是本类的对象
class Student66 implements Person{
@Override
public void study() {
System.out.println("好好学习");
}
}
//测试类
public class InnerClassDemo1 {
public static void main(String[] args) {
//想要调方法,先创建对象
PersonDemo pd = new PersonDemo();
Student66 s = new Student66();
pd.fun(s);
}
}
实现接口的方法二(高级):接口传参、使用匿名对象
//在开发中,会有人提供一个接口给我们
interface Person{
public abstract void study();
}
//定义一个类
class PersonDemo{
//创建一个方法,接口作为形参传入进去
//当接口作为方法的参数的时候,实际上需要的是实现该接口类的对象
public void fun(Person p){
p.study();
}
}
//定义一个类,来实现该接口
//当接口作为方法的参数的时候,需要的就是本类的对象
class Student66 implements Person{
@Override
public void study() {
System.out.println("好好学习");
}
}
//测试类
public class InnerClassDemo1 {
public static void main(String[] args) {
//想要调方法,先创建对象
PersonDemo pd = new PersonDemo();
// Student66 s = new Student66();
pd.fun(new Student66());
}
}
实现接口的方法三(更高级1):使用匿名内部类
不需要专门写一个类来实现接口
//在开发中,会有人提供一个接口给我们
interface Person{
public abstract void study();
}
//定义一个类
class PersonDemo{
//创建一个方法,接口作为形参传入进去
//当接口作为方法的参数的时候,实际上需要的是实现该接口类的对象
public void fun(Person p){
p.study();
}
}
//测试类
public class InnerClassDemo1 {
public static void main(String[] args) {
//想要调方法,先创建对象
PersonDemo pd = new PersonDemo();
pd.fun(new Person() { //直接调取fun方法,将匿名内部类当参数传入进去
@Override
public void study() {
System.out.println("好好学习");
}
});
}
}
实现接口的方法三(更高级2):使用构造方法
//在开发中,会有人提供一个接口给我们
interface Person{
public abstract void study();
}
//定义一个类
class PersonDemo{
Person p;
//构造方法
PersonDemo(Person p){
this.p = p;
}
}
//测试类
public class InnerClassDemo1 {
public static void main(String[] args) {
//使用匿名内部类的形式创建对象,并将匿名内部类当参数传入进去
PersonDemo pd = new PersonDemo(new Person() {
@Override
public void study() {
System.out.println("好好学习");
}
});
Person p = pd.p; //pd.p.var回车
p.study();
/*
或者
PersonDemo pd = new PersonDemo(new Person() {
@Override
public void study() {
System.out.println("好好学习");
}
}).p.study();
*/
}
}
六、匿名内部类面试题
/*
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台输出”HelloWorld”
分析:
1、method()方法可以通过类名直接调用➡说明method()方法是静态的
2、调用完method()方法之后又可以去 .show() 方法,而show()方法恰好是接口Inter中的方法
➡说明method()方法是有返回值的,而且返回值类型是Inter类型
*/
interface Inter2 {
void show();
}
class Outer8 {
//1、推出第一个结论:method方法是静态的
//2、推出第二个结论:由于main方法中调用完method方法之后还能继续调用方法
//所以得出method方法是有返回值的,由于show方法恰好是接口Inter2中的方法,所以返回值类型
//是接口Inter2类型
public static Inter2 method(){
//使用匿名内部类的形式
return new Inter2() {
@Override
public void show() {
System.out.println("HelloWorld");
}
};
}
}
public class InnerClassDemo9 {
public static void main(String[] args) {
Outer8.method().show();
}
}

浙公网安备 33010602011771号