JavaTutorials之Nested Classes

Nested class格式:

class OuterClass {
    ...
    class InnerClass {
        ...
    }
    static class StaticNestedClass {
        ...
    }
}

在这里插入图片描述
不加static的可以访问类内成员,加了的不可以访问。从内存的角度来说,static直接放在内存里了,对象得new出来才能使用,你不能访问一个还不存在得到对象。

Why Use Nested Classes?
在这里插入图片描述
1.如果一个类只对某一个类有用,那么这个类更像是工具类
2.增强了封装性,内部类可以访问某个类的所有成员,包括私有成员,B可以从外面隐藏起来。
3.使代码可读性更高,将小类放在需要使用它的地方。

Inner Classes

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

在这里插入图片描述
普通内部类可直接访问out成员,普通内部类不能包括static

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

声明方法。

Static Nested Classes
在这里插入图片描述
为了方便打包而存在:实际上,静态嵌套类在行为上是为了方便打包而嵌套在另一个顶级类中的顶级类。
初始化方法就和初始化顶级类一样:

StaticNestedClass staticNestedObject = new StaticNestedClass();

Inner Class and Nested Static Class Example
举个例子:
OuterClass.java

public class OuterClass {

    String outerField = "Outer field";
    static String staticOuterField = "Static outer field";

    class InnerClass {
        void accessMembers() {
            System.out.println(outerField);
            System.out.println(staticOuterField);
        }
    }

    static class StaticNestedClass {
        void accessMembers(OuterClass outer) {
            // Compiler error: Cannot make a static reference to the non-static
            //     field outerField
            // System.out.println(outerField);
            System.out.println(outer.outerField);
            System.out.println(staticOuterField);
        }
    }

    public static void main(String[] args) {
        System.out.println("Inner class:");
        System.out.println("------------");
        OuterClass outerObject = new OuterClass();
        OuterClass.InnerClass innerObject = outerObject.new InnerClass();
        innerObject.accessMembers();

        System.out.println("\nStatic nested class:");
        System.out.println("--------------------");
        StaticNestedClass staticNestedObject = new StaticNestedClass();        
        staticNestedObject.accessMembers(outerObject);
        
        System.out.println("\nTop-level class:");
        System.out.println("--------------------");
        TopLevelClass topLevelObject = new TopLevelClass();        
        topLevelObject.accessMembers(outerObject);                
    }
}

TopLevelClass.java

public class TopLevelClass {

    void accessMembers(OuterClass outer) {     
        // Compiler error: Cannot make a static reference to the non-static
        //     field OuterClass.outerField
        // System.out.println(OuterClass.outerField);
        System.out.println(outer.outerField);
        System.out.println(OuterClass.staticOuterField);
    }  
}
Inner class:
------------
Outer field
Static outer field

Static nested class:
--------------------
Outer field
Static outer field

Top-level class:
--------------------
Outer field
Static outer field
static class StaticNestedClass {
    void accessMembers(OuterClass outer) {
       // Compiler error: Cannot make a static reference to the non-static
       //     field outerField
       System.out.println(outerField);
    }
}

这样直接访问非静态成员变量是行不通的

System.out.println(outer.outerField);

要这样,有一个对象引用(也就是说你这个对象在内存中必须存在)。

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

要访问类内的东西加this,同名会有最近作用域

Serialization
在这里插入图片描述
谨慎对inner classes序列化。

Inner Class Example

public class DataStructure {
    
    // Create an array
    private final static int SIZE = 15;
    private int[] arrayOfInts = new int[SIZE];
    
    public DataStructure() {
        // fill the array with ascending integer values
        for (int i = 0; i < SIZE; i++) {
            arrayOfInts[i] = i;
        }
    }
    
    public void printEven() {
        
        // Print out values of even indices of the array
        DataStructureIterator iterator = this.new EvenIterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }
        System.out.println();
    }
    
    interface DataStructureIterator extends java.util.Iterator<Integer> { } 

    // Inner class implements the DataStructureIterator interface,
    // which extends the Iterator<Integer> interface
    
    private class EvenIterator implements DataStructureIterator {
        
        // Start stepping through the array from the beginning
        private int nextIndex = 0;
        
        public boolean hasNext() {
            
            // Check if the current element is the last in the array
            return (nextIndex <= SIZE - 1);
        }        
        
        public Integer next() {
            
            // Record a value of an even index of the array
            Integer retValue = Integer.valueOf(arrayOfInts[nextIndex]);
            
            // Get the next even element
            nextIndex += 2;
            return retValue;
        }
    }
    
    public static void main(String s[]) {
        
        // Fill the array with integer values and print out only
        // values of even indices
        DataStructure ds = new DataStructure();
        ds.printEven();
    }
}
The output is:

0 2 4 6 8 10 12 14 

一个例子
在这里插入图片描述

  • Local Classes

方法体里的类

Declaring Local Classes
你可以在任何方法体里定义local class

public class LocalClassExample {
  
    static String regularExpression = "[^0-9]";
  
    public static void validatePhoneNumber(
        String phoneNumber1, String phoneNumber2) {
      
        final int numberLength = 10;
        
        // Valid in JDK 8 and later:
       
        // int numberLength = 10;
       
        class PhoneNumber {
            
            String formattedPhoneNumber = null;

            PhoneNumber(String phoneNumber){
                // numberLength = 7;
                String currentNumber = phoneNumber.replaceAll(
                  regularExpression, "");
                if (currentNumber.length() == numberLength)
                    formattedPhoneNumber = currentNumber;
                else
                    formattedPhoneNumber = null;
            }

            public String getNumber() {
                return formattedPhoneNumber;
            }
            
            // Valid in JDK 8 and later:

//            public void printOriginalNumbers() {
//                System.out.println("Original numbers are " + phoneNumber1 +
//                    " and " + phoneNumber2);
//            }
        }

        PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
        PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
        
        // Valid in JDK 8 and later:

//        myNumber1.printOriginalNumbers();

        if (myNumber1.getNumber() == null) 
            System.out.println("First number is invalid");
        else
            System.out.println("First number is " + myNumber1.getNumber());
        if (myNumber2.getNumber() == null)
            System.out.println("Second number is invalid");
        else
            System.out.println("Second number is " + myNumber2.getNumber());

    }

    public static void main(String... args) {
        validatePhoneNumber("123-456-7890", "456-7890");
    }
}
First number is 1234567890
Second number is invalid

Accessing Members of an Enclosing Class
在这里插入图片描述
1.本地类可以访问类成员
2.本地类可以访问final或者 effectively final 的(方法)本地变量

在这里插入图片描述
本地类是非静态的,因为它能访问块里的成员变量,如果是static的,成员变量还没new出来就访问,个屁求了。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以有static的,但这些static必须得是final常量。final常量可以理解为和你这个类没什么关系。

Syntax of Anonymous Classes 匿名内部类语法

   HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };
  • 匿名类可以访问其封闭类的成员。
  • 匿名类不能访问其封闭作用域中未声明为final或有效final的局部变量。
  • 不能在匿名类中声明static initializers 或成员接口。
  • 匿名类可以有静态成员,只要它们是常量变量。

Lambda Expressions

对于某些类他可能只有一个方法,匿名内部类也不够简洁,使用lambda表达式可以使得代码最大程度简洁。

Ideal Use Case for Lambda Expressions

Approach 1: Create Methods That Search for Members That Match One Characteristic

public static void printPersonsOlderThan(List<Person> roster, int age) {
    for (Person p : roster) {
        if (p.getAge() >= age) {
            p.printPerson();
        }
    }
}

代码可复用性差,更改个需求,按照性别筛选人你这个就不管用了

Approach 2: Create More Generalized Search Methods

public static void printPersonsWithinAgeRange(
    List<Person> roster, int low, int high) {
    for (Person p : roster) {
        if (low <= p.getAge() && p.getAge() < high) {
            p.printPerson();
        }
    }
}

还是可扩展性差

Approach 3: Specify Search Criteria Code in a Local Class

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

interface CheckPerson {
    boolean test(Person p);
}

class CheckPersonEligibleForSelectiveService implements CheckPerson {
    public boolean test(Person p) {
        return p.gender == Person.Sex.MALE &&
            p.getAge() >= 18 &&
            p.getAge() <= 25;
    }
}

printPersons(
    roster, new CheckPersonEligibleForSelectiveService());

这种引入另一个接口使得可扩展性提升,但是带来的问题就是需要维护接口代码,以及 implement接口的类。

Approach 4: Specify Search Criteria Code in an Anonymous Class

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

这种匿名内部类式的直接调用稍微有些冗余bulky

Approach 5: Specify Search Criteria Code with a Lambda Expression

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

The CheckPerson interface is a functional interface. A functional interface is any interface that contains only one abstract method
功能性接口可以用lambda表达式代替。

Approach 6: Use Standard Functional Interfaces with Lambda Expressions

interface CheckPerson {
    boolean test(Person p);
}

CheckPerson接口是一个functional interface 因为它只有一个方法。

interface Predicate<T> {
    boolean test(T t);
}

java JDK 中含有Predicate generic interface 通用接口返回值是一个Boolean类型

public static void printPersonsWithPredicate(
    List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

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

上述为调用方法

Approach 7: Use Lambda Expressions Throughout Your Application

public static void processPersons(
    List<Person> roster,
    Predicate<Person> tester,
    Consumer<Person> block) {
        for (Person p : roster) {
            if (tester.test(p)) {
                block.accept(p);
            }
        }
}

processPersons(
     roster,
     p -> p.getGender() == Person.Sex.MALE
         && p.getAge() >= 18
         && p.getAge() <= 25,
     p -> p.printPerson()
);

consumer中包含accept函数,没有任何返回值,可用于字符串输出

void accept(T t)
public static void processPersonsWithFunction(
    List<Person> roster,
    Predicate<Person> tester,
    Function<Person, String> mapper,
    Consumer<String> block) {
    for (Person p : roster) {
        if (tester.test(p)) {
            String data = mapper.apply(p);
            block.accept(data);
        }
    }
}
processPersonsWithFunction(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25,
    p -> p.getEmailAddress(),
    email -> System.out.println(email)
);
Function<T,R>

R apply(T t)

Function<T,R>中包含 R apply(T t) R是你可以定义的返回值类型

Approach 8: Use Generics More Extensively

综上所述

public static <X, Y> void processElements(
    Iterable<X> source,
    Predicate<X> tester,
    Function <X, Y> mapper,
    Consumer<Y> block) {
    for (X p : source) {
        if (tester.test(p)) {
            Y data = mapper.apply(p);
            block.accept(data);
        }
    }
}

processElements(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25,
    p -> p.getEmailAddress(),
    email -> System.out.println(email)
);

我们注意List也是一种 Iterable类型 list extends collection extends iterable

Approach 9: Use Aggregate Operations That Accept Lambda Expressions as Parameters

使用聚合操作

roster
    .stream()
    .filter(
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25)
    .map(p -> p.getEmailAddress())
    .forEach(email -> System.out.println(email));

.stream()获得对象来源
.filter获取满足Predicate的对象
.map相当于 Function<V,T>
.forEach相当于Consumer

Accessing Local Variables of the Enclosing Scope

import java.util.function.Consumer;
 
public class LambdaScopeTest {
 
    public int x = 0;
 
    class FirstLevel {
 
        public int x = 1;
        
        void methodInFirstLevel(int x) {

            int z = 2;
             
            Consumer<Integer> myConsumer = (y) -> 
            {
                // The following statement causes the compiler to generate
                // the error "Local variable z defined in an enclosing scope
                // must be final or effectively final" 
                //
                // z = 99;
                
                System.out.println("x = " + x); 
                System.out.println("y = " + y);
                System.out.println("z = " + z);
                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
z = 2
this.x = 1
LambdaScopeTest.this.x = 0
Consumer<Integer> myConsumer = (x) -> {
    // ...
}

不能用类里的变量

Consumer<Integer> myConsumer = (y) -> {
    z = 99;
    // ...
}

只能使用类中final 或者等价final的变量,你这个给Z赋值了可是不行

Target Typing

public interface Runnable {
    void run();
}

public interface Callable<V> {
    V call();
}
void invoke(Runnable r) {
    r.run();
}

<T> T invoke(Callable<T> c) {
    return c.call();
}

String s = invoke(() -> "done");

The method invoke(Callable) will be invoked because that method returns a value;
动态确定lambda表达式的类型

Method References 方法引用
使用lambda表达式创建匿名方法。但是,有时lambda表达式只调用现有方法。在这些情况下,按名称引用现有方法通常更清晰。方法引用使您能够做到这一点;对于已经有名称的方法,它们是紧凑、易于阅读的lambda表达式。

public class Person {

    // ...
    
    LocalDate birthday;
    
    public int getAge() {
        // ...
    }
    
    public LocalDate getBirthday() {
        return birthday;
    }   

    public static int compareByAge(Person a, Person b) {
        return a.birthday.compareTo(b.birthday);
    }
    
    // ...
}
Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);

class PersonAgeComparator implements Comparator<Person> {
    public int compare(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
        
Arrays.sort(rosterAsArray, new PersonAgeComparator());

如果想比较年龄,传统写法如上述,

static <T> void sort(T[] a, Comparator<? super T> c)

Comparator是一个functional interface 可以用lambda表达式代替

Arrays.sort(rosterAsArray,
    (Person a, Person b) -> {
        return a.getBirthday().compareTo(b.getBirthday());
    }
);
Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);

注意到Person类中已经有了比较方法,所以这里可以直接调用,

Lambda表达式可以调用一个现有的方法

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

和前面的代码是等价的

Kinds of Method References 方法引用的种类

类的静态方法的引用
特定实例方法的引用
对特定类型的任意对象的实例方法的引用 String::concat
对new的引用

import java.util.function.BiFunction;

public class MethodReferencesExamples {
    
    public static <T> T mergeThings(T a, T b, BiFunction<T, T, T> merger) {
        return merger.apply(a, b);
    }
    
    public static String appendStrings(String a, String b) {
        return a + b;
    }
    
    public String appendStrings2(String a, String b) {
        return a + b;
    }

    public static void main(String[] args) {
        
        MethodReferencesExamples myApp = new MethodReferencesExamples();

        // Calling the method mergeThings with a lambda expression
        System.out.println(MethodReferencesExamples.
            mergeThings("Hello ", "World!", (a, b) -> a + b));
        
        // Reference to a static method
        System.out.println(MethodReferencesExamples.
            mergeThings("Hello ", "World!", MethodReferencesExamples::appendStrings));

        // Reference to an instance method of a particular object        
        System.out.println(MethodReferencesExamples.
            mergeThings("Hello ", "World!", myApp::appendStrings2));
        
        // Reference to an instance method of an arbitrary object of a
        // particular type
        System.out.println(MethodReferencesExamples.
            mergeThings("Hello ", "World!", String::concat));
    }
}

Enum Types 枚举类型

枚举类型是一种特殊的数据类型,它使变量成为一组预定义的常量。变量必须等于为其预定义的值之一

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY 
}

所有枚举都隐式继承java.lang.Enum。因为一个类只能扩展一个父类(请参见声明类),所以Java语言不支持状态的多重继承(请参见状态、实现和类型的多重继承),因此枚举不能继承任何其他类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值