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语言不支持状态的多重继承(请参见状态、实现和类型的多重继承),因此枚举不能继承任何其他类。