初见Java中的内部类

目录

内部类

内部类概念

内部类的设计也是一种封装的思想

内部类存在的意义

内部类的种类

1. 成员内部类

1.1 成员内部类定义

关于成员内部类产生对象的两种方式 

1.2 成员内部类需要依赖外部类的对象

2.  静态内部类

3. 方法内部类

4. 匿名内部类

Lambda表达式

Lambda表达式的四种情况

1. 无返回值无参数

2. 无返回值有参数

3. 有返回值无参数

4. 有返回值有参数  


内部类

内部类概念

所谓的内部类就是将一个类嵌套到另一个类的内部的操作。
看起来很复杂其实很简单——>就把内部类当作类中一个普通属性来看待。

普通类的包含:由一系列的属性和方法组成。


类比现实生活中:心脏和人体的关系,心脏也有很多属性和方法,心脏之于人体就是一个内部类;发动机和汽车的关系,发动机有很多属性和方法,发动机之于汽车就是一个内部类。

内部类的设计也是一种封装的思想

封装体现的就是保护性易用性
心脏封装到人类的内部——>保护

发动机封装到汽车的内部——>易用

内部类存在的意义

1. 内部类和外部类可以方便的访问彼此的私有域(属性和方法)
2. 内部类可以对外部类的外部完全隐藏(把内部类就当做外部类的属性来看待)

类的访问修饰符private(只有内部类可以使用)< default < protected < public

3. 内部类可以变相实现多继承(了解即可,很少使用)

class A;
class B;
class C{
    class InnerA extends A;
    class InnerB extends B;
}

变相实现多继承,不推荐这种方式。

内部类的种类

成员内部类
静态内部类
方法内部类
匿名内部类(Lambda表达式)

1. 成员内部类

1.1 成员内部类定义

在外部类的内部,不使用static关键字定义的内部类,就是成员内部类。——类比成员属性和成员方法。

成员内部类需要依赖外部类对象,先有外部类对象,才有内部类对象。

例如:心脏就是一个成员内部类,需要依赖外部类对象才能存在。

public class Outter {
    // 外部类Outter的成员变量
    private String msg = "Outter中的msg属性";
    private static int age = 0;

    public void fun() {
        System.out.println("Outter中的fun方法");
        Inner inner = new Inner();
        inner.fun();
        System.out.println(inner.innerMsg);
    }
    // ---------------------------------------------
    // 成员内部类
    class Inner {
        private String innerMsg = "Inner中的msg属性";

        public void fun() {
            System.out.println("Inner中的fun方法");
            // 访问本类(内部类)的innerMsg属性
            System.out.println(innerMsg);
            // 能否访问外部类的私有属性
            // 心脏能否访问人体的私有属性?-血液
            // 发动机能否访问汽车的私有属性-汽油?
            // 成员内部类需要依赖外部对象,先产生外部类对象,才有内部类对象 
            // 当产生内部类时,外部类的对象会被JVM传入进来
            System.out.println(msg);
        }
    }
    // ---------------------------------------------
    public static void main(String[] args) {
        Outter outter = new Outter();
        outter.fun();
    }
}

 

关于成员内部类产生对象的两种方式 

1. 在外部类的内部,产生成员内部类对象

在Outter的内部产生Inner对象,就和普通类没区别。

内部类名称 内部类引用= new 内部类();


2. 若内部类对外部可见(非private修饰的内部类),在外部类的外部创建内部类对象

//外部类名称.内部类名称 内部类引用 = new外部类().new内部类();
Outter.Inner inner = new Outter().new lnner();


一旦成员内部类使用private修饰,变成私有内部类,对于Outter类的外部完全隐藏,出了Outter类,外部完全无法使用Inner类。

 

1.2 成员内部类需要依赖外部类的对象

先产生外部类对象,然后产生内部类对象——类比成员属性或成员方法。

成员方法:可以访问类中的实例变量和静态变量,但是不能定义静态变量。
静态方法:可以访问类中静态变量,不可以访问成员变量。


成员内部类可以直接访问外部类的私有属性

public class Outter {
    // 外部类Outter的成员变量
    private String msg = "Outter中的msg属性";
    private static int age = 0;

    public void fun() {
        System.out.println("Outter中的fun方法");
        Inner inner = new Inner();
        inner.fun();
        System.out.println(inner.innerMsg);
    }
    // ---------------------------------------------
    // 成员内部类
    private class Inner {
        private String innerMsg = "Inner中的msg属性";

        public void fun() {
            System.out.println("Inner中的fun方法");
            // 访问本类(内部类)的innerMsg属性
            System.out.println(innerMsg);
            // 能否访问外部类的私有属性
            // 心脏能否访问人体的私有属性?-血液
            // 发动机能否访问汽车的私有属性-汽油?
            // 成员内部类需要依赖外部对象,先产生外部类对象,才有内部类对象 
            // 当产生内部类时,外部类的对象会被JVM传入进来
            System.out.println(msg);

            // 外部类对象
            System.out.println(Outter.this);
            // 内部类对象
            System.out.println(this);
        }
    }
    // ---------------------------------------------
    public static void main(String[] args) {
        Outter outter = new Outter();
        System.out.println(outter);
        outter.fun();
    }
}


成员内部类访问外部类的成员方法

public class Outter {
    // 外部类Outter的成员变量
    private String msg = "Outter中的msg属性";
    private static int age = 0;

    public void fun() {
        System.out.println("Outter中的fun方法");
    }

    public void test() {
        Inner inner = new Inner();
        inner.fun();
    }

    // ---------------------------------------------
    // 成员内部类
    private class Inner {
        private String innerMsg = "Inner中的msg属性";

        public void fun() {
            System.out.println("Inner中的fun方法");
            // 访问本类(内部类)的innerMsg属性
            System.out.println(innerMsg);
            // 成员内部类需要依赖外部对象,先产生外部类对象,才有内部类对象 
            // 当产生内部类时,外部类的对象会被JVM传入进来
            System.out.println(msg);

            // 外部类对象
            System.out.println(Outter.this);
            // 调用外部类Outter的fun方法
            Outter.this.fun();
            // 内部类对象
            System.out.println(this);
        }
    }

    // ---------------------------------------------
    public static void main(String[] args) {
        Outter outter = new Outter();
        outter.test();
    }
}

 


问题:成员内部类 能否访问外部类中的静态域?

答:可以。成员内部类已经拥有了外部类的对象。都有对象了,当然能访问静态属性。

public class Outter {
    // 外部类Outter的成员变量
    private String msg = "Outter中的msg属性";
    private static int age = 0;

    public void fun() {
        System.out.println("Outter中的fun方法");
    }

    public void test() {
        Inner inner = new Inner();
        inner.fun();
    }
    // ---------------------------------------------
    // 成员内部类
    private class Inner {
        private String innerMsg = "Inner中的msg属性";

        public void fun() {
            System.out.println("Inner中的fun方法");

             //访问外部类的成员变量和静态方法
            System.out.println(msg);
            System.out.println(age);
        }
    }
    // ---------------------------------------------
    public static void main(String[] args) {
        Outter outter = new Outter();
        outter.test();
    }
}

问题:能否在当前内部类中自己定义静态域?

答:不可以。假设可以,成员内部类的静态属性不需要产生对象就能调用,成员内部类必须要依赖外部类对象才能产生,此处矛盾!

2.  静态内部类

静态内部类:使用static关键字定义在另一个类的内部的类——类比静态方法或属性
与成员内部类最大的区别:静态内部类不需要外部类的对象,和外部类是一个相对独立的关系,只是套在外部类的内部定义而已。


问题1:静态内部类中能否直接访问外部类的成员属性?

答:没有外部类对象,无法访问外部域
问题2:静态内部类中能否访问外部类的静态属性?

答:可以

静态内部类只能访问外部类的静态域,没有外部类对象,无法直接访问外部类的成员域,


问题:静态内部类中能否定义自己的成员域?
答:可以。静态内部类就是个普通类,只不过套在一个类的内部而已。

成员内部类与静态内部类的关系
1. 成员内部类可以访问外部类的所有域(成员,静态),因为成员内部类依赖外部类的对象;但是不能拥有自己的静态域。
2. 静态内部类可以拥有自己的成员域,但是不能直接访问外部类的成员域(没有外部类对象)。

3. 方法内部类

方法内部类:定义在方法中的类

要求:

1. 不能使用任何权限修饰符(这个类出了方法就没了)。

2. 对外部完全隐藏(外部类的外部)。


3. Test内部类要使用fun方法的形参或者局部变量,该变量必须为隐式的final声明。

4. 若方法内部类中读取了方法中的局部变量(形参或者局部变量),这个局部变量就无法修改(不仅在内部类中没法改,在方法中也不能改)。

5. 方法内部类中无法定义static域


定义方法内部类

public class MethodClass {
    public static void main(String[] args) {
        fun(20);
    }

    public static void fun(int num) {
        // 局部变量
        int i = 10;
        // 方法内部类,直接定义在方法内部的类
        // 与普通代码块有点像
        class Inner {
            void test() {
                System.out.println(i);
                System.out.println(num);
            }
        }
        Inner in = new Inner();
        in.test();
    }
}

 


方法内部类要使用外部方法中的形参或者局部变量,只有读取权限,无法修改。


若方法内部类中读取了方法中的局部变量(形参或者局部变量),这个局部变量就无法修改(不仅在内部类中没法改,在方法中也不能改)。


问题:在方法内部类lnner能否定义static域? 在Java方法中能否定义static变量?

答:不能。方法内部类就看作外部方法的局部变量,Java方法中不能定义static变量。

4. 匿名内部类

匿名内部类:定义在方法中(方法的形参或者实参)——实参用的最多
没有任何权限修饰符,甚至连类名称都没有的内部类。

匿名内部类也是方法内部类的一种,最多用在方法形参中。

public class NoNameClass {
    public static void main(String[] args) {
        fun(new IMessage() { //实现了IMessage接口
            @Override
            public void getMsg(String msg) { //覆写了getMsg方法

            }
        });
    }

    public static void fun(IMessage message) {
        message.getMsg("测试匿名内部类");
    }
}

interface IMessage {     //接口IMessage
    void getMsg(String msg);
}

接口无法直接实例化对象的,因此我们此处new的是IMessage接口的子类,只不过这个子类只在fun方法中使用一次。


就是一个匿名内部类,实现了Comparator接口,覆写了compare方法。

Lambda表达式

Lambda表达式:2008年JDK8发布,划时代的版本

Hadoop Map Reduce 里面就是各种Lambda表达式的使用。

1. Java的面向对象定义太繁琐,在处理各种数学运算时,频繁需要定义类,对象,方法等。解决方法:引入函数式编程思想。


2. 在接口中引入了default普通方法——>JDK8之后的接口才有。

JDK8之前,接口中只有全局常量和抽象方法。

来源:当Java发布到2008年的时候,Java已经走过了20个年份。Java的开发者发现,上古版本中(JDK1.0)的接口的子类都具备一个普通方法,这个方法,在接口中不存在。然而JDK8之前的接口都是抽象方法,所有子类都必须覆写该方法。想在接口拓展这个方法,就引入了default普通方法。

public interface NewInterface {
    // test仍然是一个抽象方法,没有方法体
    void test();

    default void test1() {
        System.out.println("接口中的普通方法");
    }
}

class InterFaceImpl implements NewInterface {
    @Override
    public void test() {

    }
}

class Main {
    public static void main(String[] args) {

    }
}

default关键字在接口中表示普通方法,不写default就认为是抽象方法,关键字不能省略。
现在写的接口就是抽象方法+全局常量,default方法暂不考虑。


3. 函数式接口∶一个接口有且只有一个抽象方法,这种接口称之为函数式接口

@FunctionalInterface

使用注解检查当前接口是否是函数式接口,是否只包含一个抽象方法(有且只有一个)。

@FunctionalInterface
public interface FunInterface {
    void test();
}

抽象方法不为一个时,报错。

可以放入普通方法

@FunctionalInterface
interface FuncInterface {
    void test();
}
public class LambdaTest {
    public static void main(String[] args) {
        //匿名内部类
        fun(new FuncInterface() {
            @Override
            public void test() {
                System.out.println("匿名内部类实现了FuncInterface接口");
            }
        });
        fun(() -> {
            System.out.println("Lambda表达式实现了FuncInterface接口");
        });
    }
    public static void fun(FuncInterface f1) {
        f1.test();
    }
}

Lambda表达式把匿名内部类中多余的new方法定义之类的全都省略了,只保留了方法的参数和方法体的实现。

能使用Lambda表达式的前提就是接口必须是函数式接口,只有唯一的一个抽象方法。

使用多个抽象方法即报错

Lambda表达式的四种情况

看见 () -> 第一反应这是个Lambda表达式,然后去找接口中抽象方法的定义,一一对应参数和内容即可。

1. 无返回值无参数

    public static void main(String[] args) {
        // 双无的对象
        NoParaNoReturn doubleNo = () ->{
                System.out.println("无返回值,无参数的接口对象");
        };
        doubleNo.test();
        doubleNo.test();
    }

interface NoParaNoReturn {
    void test();
}

规则1:若方法体只有一行代码,可以省略大括号{}

    public static void main(String[] args) {
        // 双无的对象
        // 规则1:若方法体只有一行代码,可以省略{}
        NoParaNoReturn doubleNo = () -> System.out.println("无返回值,无参数的接口对象");
        doubleNo.test();
    }

2. 无返回值有参数

    public static void main(String[] args) {
        //无返回值有参数
        HasParaNoReturn hasParaNoReturn = (int x) ->{
            x += 20;
            System.out.println(x);
        };
        hasParaNoReturn.test(10);
    }

interface HasParaNoReturn {
    void test(int a);
}

规则2:若方法的参数只有一个,可以省略小括号()

规则3:可以省略Lambda表达式中参数的类型,若省略类型,都需要省略。

3. 有返回值无参数

    public static void main(String[] args) {
        //有返回值无参数
        NoParaHasReturn noParaHasReturn = () -> {
            int a = 10;
            int b = 20;
            return a + b;
        };
        System.out.println(noParaHasReturn.test());
    }

interface NoParaHasReturn {
    int test();
}

规则4:若抽象方法存在返回值且覆写的方法体只有一行,此时方法体的大括号,return都能省略

4. 有返回值有参数  

    public static void main(String[] args) {
        //有返回值有参数
        HasParaHasReturn hasParaHasReturn = (x, y) -> x += y;
        System.out.println(hasParaHasReturn.test(20, 30));
    }

interface HasParaHasReturn {
    int test(int a, int b);
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瘦皮猴117

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值