嵌套类

目录

一、介绍

二、嵌套类

2.1、静态内部类

2.2、内部类

2.3、Shadowing(隐藏)

2.4、序列化

三、局部类

四、匿名类

五、Lambda表达式

六、方法引用

参考


一、介绍

嵌套类分为两种:静态嵌套类和内部类。内部类中又有两种特殊的内部类:局部类和匿名类。如果局部类只使用一次,则使用匿名类更简洁。如果实现的接口只有一个方法,则lambda表达式更简洁。如果已存在可用方法,则方法引用更简洁。。。

二、嵌套类

这里不谈及特殊的内部类。嵌套类作为类的成员,可以被所有访问修饰符修饰(private、public、protected、包私有)。被static修饰的为静态嵌套类,无static修饰的为内部类。

2.1、静态内部类

静态类行为上和顶层的类相似,不能访问外部类的成员,只能访问静态成员。

如果要在外部类之外访问静态内部类,需要写出外部类名:

OuterClass.StaticNestedClass nestedObject =new OuterClass.StaticNestedClass();

注意:接口天生就是静态的

2.2、内部类

内部类可以访问外部类所有成员,但是内部类不能定义静态成员。

如果要在外部类之外访问内部类,则:

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

注意:有两种特殊的内部类:局部类和匿名类。

2.3、Shadowing(隐藏)

一个语句块就是一个作用域,作用域内的名字隐藏了作用域外的名字。

public class ShadowTest {

    public int x = 0;

    class FirstLevel {

        public int x = 1;

        void methodInFirstLevel(int x) {
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
        }
    }

    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}

输出:

x = 23
this.x = 1
ShadowTest.this.x = 0

2.4、序列化

内部类(包括局部类和匿名类)最好不要被序列化,因为不同jvm对内部类的实现不同,因此不利于跨平台。

三、局部类

局部类被定义在语句块中(大括号中),比如方法体中。内部类可以访问外部类的成员,还可以访问局部变量和参数(必须是final修饰或者effectively final)。effectively final指的是没有被final修饰,但是也没有被修改过。这个过程被称为捕获。

局部类属于内部类,也不能被申明为static成员和不能有静态初始器,但不能被访问修饰符修饰。

局部类可以存在运行时常量。

如果局部类在静态块中声明,比如静态方法,那么局部类只能访问外部类的静态成员。

四、匿名类

匿名类能够然代码更简洁,在声明类的时候同时实例化。匿名类没有名字,因此不能声明构造函数,但是可以传递参数给被继承(实现)的类(接口)的构造函数,如:

public class App {

	public static void main(String[] arg) {
		App app=new App();
		app.new Human("Hello") {
			void say() {
				System.out.println("World");
			}
		}.say();
	}
	
	class Human{
		public Human(String s) {
			// TODO Auto-generated constructor stub
			System.out.print(s);
		}
	}
}

如上,由于声明匿名类是一个表达式(非语句),因此声明后需要加上分号。

匿名类表达式由new操作符、要继承(实现)的类名(接口名)、传给构造函数的参数(如果实现接口,则没有参数)和类体(body)构成。

和局部类一样,匿名类能够捕获变量。比如匿名类可以访问外部类的成员;能够访问fianl或effectively final的局部变量或者参数;能够隐藏外部作用域的同名变量。匿名类也不能声明静态初始器和静态成员,但是能够声明运行时常量。

注意,没有类名则不能声明构造函数。

五、Lambda表达式

如果要实现的接口(不能为类)只有一个方法,那么匿名类仍然不够简洁,因此有了Lambda表达式,如:

public class App {

	public static void main(String[] arg) {
		method(()->System.out.println("hello human"));
	}
	static void method(Human human) {
		human.say();
	}
	interface Human{
		void say() ;
	}
}

Lambda表达式由参数、->、body(主体)组成。

参数由逗号分隔,忽略参数的类型。如果只有一个参数,则可以忽略括号,如:

p -> p.getGender() == Person.Sex.MALE 
    && p.getAge() >= 18
    && p.getAge() <= 25

body(主体)为单个的表达式或者一个语句块。如果是一个表达式,jvm会计算表达式然后返回他的值,如上面所示。也可以使用return语句,由于return语句不是表达式,因此要声明语句块:

p -> {
    return p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25;
}

如果仅仅调用一个无参方法,可以省略括号和分号:

email -> System.out.println(email)

Lambda表达式不会引入新的作用域,因此Lambda的参数和body中的变量处于外部作用域中,如下面的例子:

import java.util.function.Consumer;

public class LambdaScopeTest {

    public int x = 0;

    class FirstLevel {

        public int x = 1;

        void methodInFirstLevel(int x) { 
            Consumer<Integer> myConsumer = (y) -> 
            {
                System.out.println("x = " + x); // Statement A
                System.out.println("y = " + y);
                System.out.println("this.x = " + this.x);
                System.out.println("LambdaScopeTest.this.x = " +
                    LambdaScopeTest.this.x);
            };

            myConsumer.accept(x);

        }
    }

    public static void main(String... args) {
        LambdaScopeTest st = new LambdaScopeTest();
        LambdaScopeTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}

输出:

x = 23
y = 23
this.x = 1
LambdaScopeTest.this.x = 0

其他的类似,比如只能可以访问外部类成员、捕获final和effectively fianl局部变量(参数)、隐藏其他变量等等。

Lambda表达式是什么类型(具体实现的接口)可以由编译器推断出,因为同一个Lambda表达式可以表示很多接口的实现。

六、方法引用

如果创建lambda表达式只是为了调用已存在的方法,那么lambda表达式也不够简洁。。。。因此出现了方法引用。

比如排序数组,用lambda表达式调用已存在的方法:

Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);

现在使用方法引用:

Arrays.sort(rosterAsArray, Person::compareByAge);

两者其实是一样的,只是方法引用更简洁。

下面列出了四种方法引用。

KindExample
Reference to a static methodContainingClass::staticMethodName
Reference to an instance method of a particular objectcontainingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular typeContainingType::methodName
Reference to a constructorClassName::new

 

参考

https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值