33. 内部类
分类 | 特殊点 |
---|---|
成员内部类 | 定义在成员位置,分别具有成员以及类的特点,成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象,代码实现见33.1 |
私有内部类 | 其他类无法使用!!! ,代码实现见33.2 |
静态内部类 | 只有静态内部类中能直接定义并使用静态内容,且静态内部类是不持有指向外部类对象的引用的,即只创建一个对象 ,代码实现见33.3 |
局部内部类 | 定义在局部,不能被成员修饰符修饰,只能被final修饰,代码实现见33.4 |
匿名内部类 | 属于特殊的局部内部类,但声明以及创建对应实例的方式比较特殊,代码实现见33.5 |
33.1 成员内部类
public class Outer {
private int a = 6;
private static int i = 6;
class inner {
/*
* 当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
* */
private int a = 7;
public void Use(){
//static int j = 10; 会报错,除了只有静态内部类允许直接定义静态内容
System.out.println("内部类方法");
System.out.println(a); //默认使用成员内部类自己的成员
System.out.println(this.a); //成员内部类
System.out.println(Outer.this.a); //外部类的private成员
System.out.println(i);
System.out.println("同名默认调用成员内部类中的");
// Use();
// Outer.this.Use();
System.out.println(i);
}
}
public void Use(){
System.out.println("外部类成员方法");
System.out.println(a);
/*成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。
在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问*/
System.out.println(new inner().a);
System.out.println(i);
}
}
class TestOuter {
public static void main(String[] args) {
//成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象
//即使用内部类要创建两个对象!!!
Outer.inner inner = new Outer().new inner();
inner.Use();
new Outer().Use();
}
}
33.2 私有内部类
public class Outer_2 {
private int a = 3;
static int x = 3;
//私有内部类
private class Inner{
private int b = 4;
}
public Inner getInner(){
return new Inner();
}
}
class Test{
public static void main(String[] args) {
//new Outer_2().Inner(); 报错!!!私有内部类在其他类中无法使用
Outer_2 oo = new Outer_2();
//Outer_2.Inner inner = oo.getInner(); 报错!!!可以看到即使提供了获取对象方法也无法使用!!!
}
}
33.3 静态内部类
public class Outer_3 {
static int a = 3;
int b = 4;
static class Inner{
static int c = 6;
int d = 8;
public void a(){
System.out.println(c); //内部类静态成员
System.out.println(d); //内部类成员
System.out.println(a); //外部类静态成员
System.out.println(new Outer_3().b); //外部类成员
}
public static void b(){
System.out.println(c); //内部类静态成员
System.out.println(new Inner().d); //内部类成员
System.out.println(a); //外部类静态成员
System.out.println(new Outer_3().b); //外部类成员
}
}
}
class TestA{
public static void main(String[] args) {
/*
* 静态内部类是不依赖于外部类的,也就说可以在不创建外部类对象的情况下创建内部类的对象。另外,静态内部类是不持有指向外部类对象的引用的
*/
Outer_3.Inner inner = new Outer_3.Inner(); //注意new的方式是 外部类名.内部类()
inner.a(); //内部类成员方法
Outer_3.Inner.b(); //内部类静态方法
}
}
33.4 局部内部类
public class Outer_4 {
int i = 6;
static int x = 7;
public void use(){
int a = 6;
//只能在当前方法内使用,在外部类的其他方法以及其他类中都无法使用
class Inner {
int p = 10;
int z = 11;
//static int v = 10; 报错,static只修饰成员
static final int y = 88888; //只能final和static一起使用
public void InnerUse(){
System.out.println(i);
System.out.println(x);
System.out.println(y);
System.out.println(z);
System.out.println(a);
//a++; 报错 方法中的局部变量在局部内部类中使用后就只能为常量 , 包括方法形参!!!
//JDK1.7及之前,需要手动final修饰
//JDK1.8及之后,该变量默认被final修饰
}
}
}
}
33.5 匿名内部类
匿名对象的三种使用方式
public class AnonymousInnerClass {
/*
* 匿名内部类,我的理解是只在当前作用域用到的次数不多的 接口实现类或者子类 ,不需要它的名字,只要实现对应的功能
* 比如我现在就想看一场表演,不管是谁表演
*
* 匿名内部类 可以将 定义实现类并重写方法的过程 和 创建实现类对象的过程 合二为一
*/
public static void main(String[] args) {
//匿名对象第一种使用方式
new Perform(){
@Override
public void acting() {
System.out.println("唱歌表演");
}
}.acting();
//匿名对象第二种使用方式,用对应的接口或抽象父类声明变量并接收匿名对象的引用,后续可多次使用 ( 多态 )
Perform p = new Perform(){
@Override
public void acting() {
System.out.println("跳舞表演");
}
};
p.acting();
//匿名对象作为方法实参传入
performing(new Perform() {
@Override
public void acting() {
System.out.println("摇摆");
}
});
}
public static void performing(Perform p){
p.acting();
}
}
interface Perform{
void acting();
}
34. Lambda表达式
34.1 定义及使用
Lambda 表达式是匿名内部类的简化写法。
-
JDK1.8及以后
-
作用 : 简化满足条件的匿名内部类对象
-
前提 : 实现的是函数式接口 : 接口中只有一个必须被重写的抽象方法,可存在默认方法以及静态方法等存在方法体的方法
-
检测函数式接口 : @FunctionalInterface 注解
Lambda 属于函数式编程思想。
只关注匿名内部类中最核心的这些内容(方法参数,方法体,返回值) -
将方法实现(某个行为)当做参数传递
面向对象思想:怎么做。
函数式编程思想:做什么。 -
-> Lambda符,也叫上下文推导符
34.2 代码实现
public class Lambda {
public static void main(String[] args) {
//匿名内部类实现方式
/* Study s = new Study(){
@Override
public void Test() {
System.out.println("学生学习"); // void Test();
}
};*/
//注意,都省略关键的一点,就是new 函数式接口的过程
//Lambda标准写法 ( ) -> { }
//Study s = () -> { System.out.println("学生学习");}; // void Test();
//Lambda写法2 : 方法体(无返回值)可省略{ }
//Study s = () -> System.out.println("学生学习"); // void Test();
//Lambda写法3 : 参数列表只有一个时,( )可省略,参数类型也可省略
//Study s = a -> System.out.println("学生学习"+a); // void Test(int a);
//Lambda写法4 : 参数列表有多个时,仅参数类型省略 // void Test(int a,int b);
//Study s = (x,y) -> System.out.println("学生学习"+x+y); //结果不同12
//Study s = (x,y) -> System.out.println("学生学习"+(x+y)); // 3
//s.Test(1,2);
//Lambda写法5 : 当方法体只有一句且为return带出返回值语句时,{ } 和 return可一起省略
//Study s = a -> a>=18 ; // boolean Test(int a)
//System.out.println(s.Test(16));
//Lambda用法6 : 作为实参传入方法
studying( () -> System.out.println("正在学习") );
}
public static void studying(Study s){
s.Test();
}
}
@FunctionalInterface
interface Study{
void Test();
//void Test(int a);
//void Test(int a,int b);
//boolean Test(int a);
}
34.3 方法引用
基本概念及使用:
1、写一个函数式接口时,方法的实现(lambda体),已经被某个其他的对象实现了,就不需要在Lambda体中,再次调用这个实现,而可以直接使用那个已经定义好的方法。
2、格式:
函数式接口 名称 = 对象名 :: 方法名称;
函数式接口 名称 = 类名 :: 静态方法名;
3、作用:
把已经实现的方法,作为一个数据,作为实现类对象,赋值给某个函数式接口的引用
可以把这个引用当做方法的返回值,也可以作为方法的实际参数进行传递
4、本质:
可以把任意一个方法,作为函数式接口的一个实现类对象
这里分为静态方法引用,和对象的实例方法引用
代码实现
public class TestLambda {
public static void main(String[] args) {
//1.普通Lambda实现函数式接口
A a = s -> System.out.println(s);
a.show("nuc");
//2.使用方法引用实现函数式接口
//调用实例方法
// 对象名 :: 方法名
A a1 = System.out :: println;
a1.show("edu");
a1.show("ygh");
A a2 = new InterImpl() :: fun;
a2.show("26000");
//调用静态方法
// 类名 :: 静态方法名
A a3 = InterImpl2 :: fun;
a3.show("15");
}
}
abstract interface A{
void show(String s);
}
class InterImpl{
public void fun(String s){
System.out.println(Integer.parseInt(s) + 5);
}
}
class InterImpl2{
public static void fun(String s){
System.out.println(Integer.parseInt(s) + 10);
}
}