目录
内部类
内部类概念
所谓的内部类就是将一个类嵌套到另一个类的内部的操作。
看起来很复杂其实很简单——>就把内部类当作类中一个普通属性来看待。普通类的包含:由一系列的属性和方法组成。
类比现实生活中:心脏和人体的关系,心脏也有很多属性和方法,心脏之于人体就是一个内部类;发动机和汽车的关系,发动机有很多属性和方法,发动机之于汽车就是一个内部类。
内部类的设计也是一种封装的思想
封装体现的就是保护性和易用性
心脏封装到人类的内部——>保护发动机封装到汽车的内部——>易用
内部类存在的意义
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); }