Hello World

Hello Young


  • 首页

  • 分类

  • 归档

  • 标签

  • 关于

  • 搜索

Java 学习指南_学习Java:基础-匿名类

发表于 2017-12-04 | 分类于 java

匿名类Anonymous Classes

Anonymous Classes

匿名类可以是你的代码更加简。你可以同时声明和实例化一个匿名类。匿名类与局不累很想,只不过没有名字。如果你只想使用一个局部类一次,就可以是用匿名类。

本节包含了一下主题:

  • 声明匿名类
  • 匿名类的语法
  • 访问闭合范围内的局部变量,以及声明和访问匿名类的成员
  • 匿名类的实例

声明匿名类

局不类是声明,而匿名类是表达式,意思是你在另外一个表达式中定义这个类。下面的例子,HelloWorldAnonymousClasses, 在局部变量 frenchGreeting 以及spanishGreeting的初始化过程中使用了匿名类,而 而局部变量 englishGreeting的声明则使用的局部类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class HelloWorldAnonymousClasses {
interface HelloWorld {
public void greet();
public void greetSomeone(String someone);
}
public void sayHello() {
class EnglishGreeting implements HelloWorld {
String name = "world";
public void greet() {
greetSomeone("world");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hello " + name);
}
}
HelloWorld englishGreeting = new EnglishGreeting();
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);
}
};
HelloWorld spanishGreeting = new HelloWorld() {
String name = "mundo";
public void greet() {
greetSomeone("mundo");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hola, " + name);
}
};
englishGreeting.greet();
frenchGreeting.greetSomeone("Fred");
spanishGreeting.greet();
}
public static void main(String... args) {
HelloWorldAnonymousClasses myApp =
new HelloWorldAnonymousClasses();
myApp.sayHello();
}
}

匿名类的语法Syntax of Anonymous Classes

就像之前提到的一样,一个匿名类是一个表达式。匿名类表达式的语法类似调用一个构造器,不过在后边多了定义类的代码块。

参考一下 frenchGreeting 对象的实例:

1
2
3
4
5
6
7
8
9
10
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);
}
};

匿名类表达式由一下几部分组成:

  • new 操作符
  • 一个用来实现的接口或者用来继承的类的名称。本例中,匿名类为接口HelloWorld 的实现。
  • 包含构造器参数的圆括号,类似普通的类创建实例表达式。注意: 当你实现一个接口时,没有构造器,所以你使用一个空的圆括号。
  • 一个类体,包括了类的定义。需要注意的事,在类体中可以允许方法声明,但是不允许有语句。

因为匿名类的定义是一个表达式,他必须是语句的一部分。在本例中,匿名类表达式是实例化frenchGreeting对象语句的一部分。(这也解释了为什么需要在闭合的大括号后面需要一个分号)

访问闭合氛围内的局部变量,以及声明和访问匿名类的成员

就像局不类一样,匿名类可以 捕获变量capture variables;他们对于闭合范围内的局部变量具有相同的访问权限:

  • 匿名类可以访问他的闭合类的成员。
  • 匿名类不能访问他所属闭合范围内定义不是final或者等效于与final的局部变量。
  • 与嵌套类一样,在匿名类中声明某一类型的变量会遮蔽它所属闭合范围内的其它名字相同的变量。参考 遮蔽Shadowing以获取更多信息。

匿名类与局不累一样对它自己的成员访问有一些限制:

  • 你不能在匿名类中定义静态的初始化器或者是成员接口.
  • 匿名类中可以是用静态成员来定义常量。

注意你可以在匿名类中定义以下内容:

  • 字段/属性Fields
  • 扩展方法(尽管他们没有实现任何父类的方法)
  • 实例初始化器
  • 局不类

不过你不能再匿名类中定义构造器.

匿名类的实例Examples of Anonymous Classes

匿名类经常在图形界面的应用中使用。

参考下列JavaFX 列子 HelloWorld.java (摘自 Hello World, JavaFX Style 中 Getting Started with JavaFX). 这个例子创建了一个报刊了 Say ‘Hello World’按钮的框架 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class HelloWorld extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}

本例中I,方法调用btn.setOnAction 定义了当你选择Say ‘Hello World’按钮的时候回发生什么.这个方法要求一个 EventHandler<ActionEvent>类型的对象. EventHandler<ActionEvent>接口仅包含一个方法,handle。这里没有重新定义一个类来实现这个方法,而是直接使用了匿名类表达式。注意这个表达式是传递给btn.setOnAction 方法的参数.

因为 EventHandler<ActionEvent> 接口仅仅包含一个方法,你同样可以使用lambda表达式来代替匿名类表达式. 参考Lambda Expressions 章节来获取更多信息.

匿名类是用来实现包含有两个或多个方法接口的理想方法。下面的例子JavaFX example 来自Customization of UI Controls](https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/custom.htm). 代码中创建了一个只接受数字值文本域. 它使用匿名类重新定义了TextField类,复写了继承自 TextInputControl 类的 replaceText 和 replaceSelection 方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class CustomTextFieldSample extends Application {
final static Label label = new Label();
@Override
public void start(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root, 300, 150);
stage.setScene(scene);
stage.setTitle("Text Field Sample");
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setVgap(5);
grid.setHgap(5);
scene.setRoot(grid);
final Label dollar = new Label("$");
GridPane.setConstraints(dollar, 0, 0);
grid.getChildren().add(dollar);
final TextField sum = new TextField() {
@Override
public void replaceText(int start, int end, String text) {
if (!text.matches("[a-z, A-Z]")) {
super.replaceText(start, end, text);
}
label.setText("Enter a numeric value");
}
@Override
public void replaceSelection(String text) {
if (!text.matches("[a-z, A-Z]")) {
super.replaceSelection(text);
}
}
};
sum.setPromptText("Enter the total");
sum.setPrefColumnCount(10);
GridPane.setConstraints(sum, 1, 0);
grid.getChildren().add(sum);
Button submit = new Button("Submit");
GridPane.setConstraints(submit, 2, 0);
grid.getChildren().add(submit);
submit.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
label.setText(null);
}
});
GridPane.setConstraints(label, 0, 1);
GridPane.setColumnSpan(label, 3);
grid.getChildren().add(label);
scene.setRoot(grid);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Java 学习指南_学习Java:基础-局部类

发表于 2017-10-24 | 分类于 java

局部类Local Classes

局部类是指定义在一个代码块block中的类,一组包含与{}之内的一组语句。

本节包含了以下内容:

  • 定义一个局部类Declaring Local Classes
  • 访问所属类的成员Accessing Members of an Enclosing Class
    • 遮蔽与局部类Shadowing and Local Classes
  • 局部类与内部类相似Local Classes Are Similar To Inner Classes

定义局部类Declaring Local Classes

你可以在任何代码块中定义局部类(参考 Expressions, Statements, and Blocks 表达式,语句代码块获取更多的信息).例如:你可以在一个方法体内,一个for循环内或者一个if语句中定义局部类。

下面的例子, LocalClassExample,验证了两个电话号码。他在方法validatePhoneNumber方法内定义了一个局部类PhoneNumber:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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");
}
}

这个例子通过两部验证一个电话号码,首先将电话号码中所有除了0-9的数字一处,然后检查电话号码是否包含了10个数字(北美的电话号码长度).例子的输出如下:

1
2
First number is 1234567890
Second number is invalid

局部类访问你所处类的成员Accessing Members of an Enclosing Class

局部类有访问其所处类成员的权限。前面的例子中,PhoneNumber构造器访问了成员 LocalClassExample.regularExpression. 另外,局部类有访问局部变量的权限。不过,局部类只能访问被声明未final的局部变量。当一个局部类访问一个局部变量或者时所处代码块的参数时,它捕获captures了那个变量或者参数。例如,PhoneNumber构造器可以访问局部变量numberLength因为他是定义未final的;numberLength是一个捕获变量captured variable.

然而你,Java SE 8开始,可以访问所属代码块中定义未final或者有效最终effectively final的变量以及参数.例如,假设变量numberLength没有被声明为final,并且在PhoneNumber的构造其中加了如下的代码,将其变量值改变为数字7:

1
2
3
4
5
6
7
8
9
PhoneNumber(String phoneNumber) {
numberLength = 7;
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}

因为这个赋值语句,变量numberLength就是不再时一个有效最终值。这样做的结果就是,当PhoneNumber类尝试引用局部变量 numberLength时, Java编译器生成一个错误信息,类似“local variable referenced from an inner class must be final or effectively final”(内部类中引用的局部变量必须是一个最终或或者一个等效最终值)

1
if (currentNumber.length() == numberLength)

从Java SE 8开始,你再一个方法中定义局部变量时,他可以访问方法的参数。例如,你可以再局部类PhoneNumber中定义如下方法:

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

方法printOriginalNumbers方位了方法 validatePhoneNumber的两个参数 phoneNumber1 以及phoneNumber2 。

局部类与遮蔽Shadowing and Local Classes

再局部类中声明一个类型(譬如变量)遮蔽其所在闭合范围内拥有相同名字的类型。参考遮蔽 Shadowing 获取更多信息。

局部类与内部类相似Local Classes Are Similar To Inner Classes

局部类与内部类相似,因为它们都不定义任何的静态static成员。局部类如果再静态方法内,譬如 PhoneNumber类,定义再静态方法 validatePhoneNumber中,就只能引用闭合范围内的静态成员。如果你不讲成员变量 regularExpression 定义为静态static,那么Java编译器就会生成一个错误, “non-static variable regularExpression cannot be referenced from a static context.”(非静态变量regularExpression不能再一个静态的上下文中引用)

局部类是非静态的,因为它可以方法所属闭合代码块中的实例变量。因此它们几乎不能包含任何类型的静态声明。

你不能再一个代码块中定义接口,因为接口再本质上静态的。例如,下面的代码引用是不能编译的因为接口HelloThere被定义再方法greetInEnglish方法体中:

1
2
3
4
5
6
7
8
9
10
11
12
public void greetInEnglish() {
interface HelloThere {
public void greet();
}
class EnglishHelloThere implements HelloThere {
public void greet() {
System.out.println("Hello " + name);
}
}
HelloThere myGreeting = new EnglishHelloThere();
myGreeting.greet();
}

你不能再一个局部类中定义静态的初始化器或者成员。以下的代码不能通过编译,因为方法 EnglishGoodbye.sayGoodbye 被声明为 static. 当遇到这种方法定义时编译器会生成一个错误类似 “modifier ‘static’ is only allowed in constant variable declaration”(限定符static只能用再常量的声明上) :

1
2
3
4
5
6
7
8
public void sayGoodbyeInEnglish() {
class EnglishGoodbye {
public static void sayGoodbye() {
System.out.println("Bye bye");
}
}
EnglishGoodbye.sayGoodbye();
}

局部类可以有静态成员除非时常量. (常量 constant variable是一个定义为final的基本数据类型或者String字符串类型的变量初始化为一个编译时的常量表达式。编译时常量表达式同时时一个字符串或者时编译时可以计算的运算表达式。参考理解成员变量Understanding Class Members 获取更多信息.) 以下代码摘绿可以通过编译,因为静态成员 EnglishGoodbye.farewell 是一个常量:

1
2
3
4
5
6
7
8
9
10
public void sayGoodbyeInEnglish() {
class EnglishGoodbye {
public static final String farewell = "Bye bye";
public void sayGoodbye() {
System.out.println(farewell);
}
}
EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
myEnglishGoodbye.sayGoodbye();
}

Java 学习指南_学习Java:基础-内部类

发表于 2017-10-24 | 分类于 java

内部类

讨论内部类的使用之前,首先来观察一个数组。接下来的例子中,你创建一个数组,使用Integer指填充这个数组,然后只升序输出这个数组中下标为偶数的值。

DataStructure.java 例子有以下几个部分组成:

  • DataStructure 外部类,包含了一个创建DataStructure实例的构造函数,实例包含一个填充了连续的整数数组(0,1,2,3…)以及一个打印出数组偶数下标元素的值的方法。
  • EvenIterator内部类,实现了DataStructureIterator接口,并继承了 Iterator< Integer> 迭代器接口。迭代器是用来一步步的迭代一个数据结构并且通常有一个测试是否到达最后一个元素的方法,获取当前的元素并将指针移向下一个元素。
  • 一个main方法,实例化一个DataStructure对象 ds,调用ds对象的printEven方法来输出arrayOfInts数组中具有偶数下边的元素值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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();
}
}

输出如下:

1
0 2 4 6 8 10 12 14

注意EvenIterator类直接引用DataStructure对象的实例变量arrayOfInts.

你可以使用内部类来实现类似以上例子中的辅助类。处理用户界面事件时,你必须直到如何使用内部类,因为事件处理机制使通过内部类来扩展其用法。

局部类与匿名内部类

有中附加的内部类。你可以在一个方法的内部定义一个类,这样的类叫做局部类 local classes. 。你同样可以在方法内定义一个未命名内部类,这样的类讲过匿名内部类 anonymous classes.。

限定修饰符

你对内部类使用同外部类其他成员一样的限定修饰符。例如,你可以使用特定的private,public,以及protected来显示对内部类的访问,同其他类的成员一样。

Java 学习指南_学习Java:基础-嵌套类

发表于 2017-10-23 | 分类于 java

嵌套类

java编程语言允许你在一个类的内部定义类。这样的类叫做嵌套类**nested class,示例如下:

1
2
3
4
5
6
class OuterClass {
...
class NestedClass {
...
}
}

术语Terminology: 嵌套类分为两类,静态和非静态.声明为static的嵌套类叫做 static nested classes静态嵌套类。非静态嵌套类叫做inner classes内部类。


1
2
3
4
5
6
7
8
9
class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}

嵌套类是其所在类的成员。非静态嵌套类(内部类)具有访问所在类的其他成员的访问权限,即使是私有的成员。静态内部类没有访问其所在类的其他成员的权限。作为OuterClass的成员,一个嵌套类可以被声明为private,public,protected或者包级私有(package private 默认)。(重申以下外部类只能 被定义为public 或者 包级私有)。

为什么使用嵌套类?

驱使使用嵌套类的原因包括以下几个:

  • 它是为只在一个地方使用的类的逻辑分类的一种方法It is a way of logically grouping classes that are only used in one place: 如果一个类支只对另外一个类有用,那么把它嵌入那个类中是两者保持在一起是符合逻辑的。嵌套进这样的辅助类 是他们的包结构更加的合理化。
  • 增加封装性It increases encapsulation: 考虑两个顶级的类,A和B,如果B需要访问A中原本声明为private的成员。通过将B吟唱在A类内,A的成员可以被定义为private私有,并且B可以访问它们,B本身也可以对于外部世界隐藏。
  • 是代码更加易读和易于管理It can lead to more readable and maintainable code: 将小类嵌套与顶级类中是代码更接近它使用的地方。

静态嵌套类Static Nested Classes

与类方法和类变量一样,静态嵌套类是与其外部类相关联的。并且像静态类方法一样,一个静态嵌套类不能直接引用类中定义的实例变量和实例方法:只能通过一个对象的引用来使用。


注意: 静态嵌套类与其外部类或者其他类的实例成员交互 与它的顶级类一样。实际上,一个静态嵌套类与与顶积累表现一样,只不过可以方便的嵌入其他定义类,构建包更加便利。

static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class. In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience.


静态嵌套类通过其所有类的类名来访问例如:

1
OuterClass.StaticNestedClass

如果需要创建一个静态嵌套类的对象,使用如下的语法:

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

内部类

与实例方法和实例变量一样,一个内部类是与其所有类的实例相关联的,并且可以直接访问其实例方法和字段/域。 同样,因为内部类是域实例向关联的,所以它本身不能定义任何的静态成员。

内部类的实例对象存在与外部类实例的内部,观察下面的类:

1
2
3
4
5
6
class OuterClass {
...
class InnerClass {
...
}
}

一个内部类 InnerClass 的实例只能存在于一个OuterClass类的实例内部,并且可以直接访问该实例的方法和字段/域。

实例化一个内部类,你必须现实里话外部类。然后,通过外部类的对象创建内部对象,语法如下:

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

内部类还有两种特殊情况:局部类 和 匿名内部类.: local classes and anonymous classes.

遮蔽Shadowing

如果在一个特定的范围内(例如在一个内部类或者是方法顶以内)定义一个类型(例如成员变量或者是参数名称)与闭合区域内(例如外部类的闭合区域内)的另外一个声明有相同的名字,那么这个声明就会遮蔽闭合区域内的声明。你不能指通过名字引用被遮蔽的声明。下面是一个例子, ShadowTest, 演示了这个现象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
}
}

以下为这个例子的输出:

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

这个例子定义了三个变量名字都为x: 类ShadowTest的成员变量,内部类FirstLevel的成员变量,以及方法methodInFirstLevel的参数。方法methodInFirstLevel的参数x遮蔽了内部类FirstLevel的变量x。因此,当你在方法methodInFirstLevel中使用x时,引用的时方法的参数。为了引用内部类FirstLevel的成员变量,使用this挂件子来代表笔和空间:

1
System.out.println("this.x = " + this.x);

为了引用更大的闭合范围内的成员变量,需要使用它所属于的类名。例如,下面的语句,在methodInFirstLevel方法中引用了ShadowTest类的成员变量:

1
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);

序列化Serialization

内部类的序列化,包括局部类和匿名内部类,强力不建议。当Java编译器编译确定的结构,例如内部类,会创建合成构造 synthetic constructs; 包括类,方法,字段,以及其他的结构,这些结构在源码中并没有对应的相应的构造。合成构造能使使Java编译器在不更换JVM的情况下实现新的Java语言特性。然而合成构造可能因为不同固定Java编译器实现而有区别,意味着.class文件可能因为不同的实现也有所不同。因此,如果你用不同的JRE实现序列化和反序列化一个内部类的时候可能会出现各种各样的复杂问题。

参考 Implicit and Synthetic Parameters 隐式合成参数, Obtaining Names of Method Parameters 获取方法参数名 章节,来获取内部类编译时生成合成构造的更多信息。

Java 学习指南_学习Java:基础-类-问题与练习

发表于 2017-10-20 | 分类于 java

类:问题与练习

问题

  1. 观察下面的类:

    1
    2
    3
    4
    public class IdentifyMyParts {
    public static int x = 7;
    public int y = 3;
    }
    1. 问题: 哪一个是类变量?

    2. 问题:哪一个是实例变量?

    3. 问题: 以下代码的输出内容是什么:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      IdentifyMyParts a = new IdentifyMyParts();
      IdentifyMyParts b = new IdentifyMyParts();
      a.y = 5;
      b.y = 6;
      a.x = 1;
      b.x = 2;
      System.out.println("a.y = " + a.y);
      System.out.println("b.y = " + b.y);
      System.out.println("a.x = " + a.x);
      System.out.println("b.x = " + b.x);
      System.out.println("IdentifyMyParts.x = " + IdentifyMyParts.x);

      ​

练习

  1. 练习: 写一个类,类的每一个实例代表了一副牌中的一张。没一张牌有两个属性:点数与花色。保留你的解决方案,在 Enum Types 枚举类一节中会要求你重写这个类.
  2. 练习: 写一个类,类的每一个实例代表一整副扑克。同样保留这个类。
  3. 练习:写一个小程序来测试你的单长牌与扑克类。可以简单的创建一副扑克并展示它其中的每一张牌。

答案

阅读全文 »

Java 学习指南_学习Java:基础-对象-问题与练习

发表于 2017-10-20 | 分类于 java

对象-问题与练习

问题

  1. 问题: 下面的程序有什么问题?

    1
    2
    3
    4
    5
    6
    7
    8
    public class SomethingIsWrong {
    public static void main(String[] args) {
    Rectangle myRect;
    myRect.width = 40;
    myRect.height = 50;
    System.out.println("myRect's area is " + myRect.area());
    }
    }
  2. 问题: 以下代码创建了一个数组对象和字符串对象。当代码实行完毕后,有多少个指向那些对象的引用?是否两个对象都将会垃圾回收器回收?

    1
    2
    3
    4
    5
    6
    ...
    String[] students = new String[10];
    String studentName = "Peter Smith";
    students[0] = studentName;
    studentName = null;
    ...
  3. 问题: 程序是如何销毁一个它创建的对象的?

练习

  1. 练习: 修复 SomethingIsWrong 代码在问题1中的错误.

  2. 练习: 已知以下类 NumberHolder, 些以下代码创建一个该类的实例,初始化它的两个成员变量,然后显示每个成员变量的值。

    1
    2
    3
    4
    public class NumberHolder {
    public int anInt;
    public float aFloat;
    }

答案

阅读全文 »

Java 学习指南_学习Java:基础-类和对象总结

发表于 2017-10-20 | 分类于 java

创建于使用类和对象的总结

类的定义 为类命名并将类体包含与{ }之内。类的命名可以加上修饰符。类体内包含了域/字段,方法,以及类的构造器。

类使用字段来包含状态信息,使用方法来表现行为。 构造器使用域类型一样但是没有返回值的方法类初始化一个类的实例。

控制类以及其访问权限的方法一样:通过声明时使用访问修饰符来确定,例如public

通过使用static关键字在成员声明时区分类变量域类方法。在声明时没有使用static关键字的成员默认为实例成员。类变量为所有类的实例所共享的,既可以通过类名引用也可以通过实例来引用。 来的实例获取每一个实例变量的副本,实例变量必须通过实例来引用。

你可以使用new操作符来从一个类创建对象。new操作符返回一个 指向创建的对象的引用。你可以将其赋值给一个变量,也可以直接使用。

实例变量或方法同样可以被类以外的代码访问,不过必须通过合格的名称来引用。一个正确的引用实例变量的代码类似如下

1
objectReference.variableName

引用实例方法的代码如下

1
objectReference.methodName(argumentList)

或者:

1
objectReference.methodName()

垃圾回收器自动清理不再被使用的对象。不在被使用的判定条件为程序不在持有该对象的引用。你可以显示的抛弃一个对象的引用,通过将变量的引用设置为指向null;

Java 学习指南_学习Java:基础-类-初始化字段

发表于 2017-10-20 | 分类于 java

初始化域

就像你看到的一样,你通常可以在一个字段/域定义的时候提供一个初始值:

1
2
3
4
5
6
7
8
public class BedAndBreakfast {
// initialize to 10
public static int capacity = 10;
// initialize to false
private boolean full = false;
}

当初始化的值可用并且初始化可以在一行内完成时这么做都没有问题。不过,这样的初始化形式有一些使用限制因为它过于简单。如果初始化需要一些逻辑(例如,错误处理或者一个for循环来填充一个复合的数组),简单的赋值就不适合了。实例变量可以在在构造器中实例化,这里也可以使用错误处理和逻辑。为了给类变量的初始化也提供这样的能力,Java编程语言包含了静态代码块 static initialization blocks.


注意: 并不是一定要在类定义的开始就声明字段/域,尽管这是最通常的做法。只是要求它们在使用前必须声明和初始化。


静态代码块

静态代码块时在一个普通的{ }包括的代码块前面加上static关键字。下面是一个例子:

1
2
3
static {
// whatever code is needed for initialization goes here
}

一个类可以有多个静态代码块,并且它们可以出现在类体body的任何地方。运行环境确保所有的静态代码块按照它们在源代码中出现的顺序被调用。

静态代码块有个可替换的方法–你可以使用私有静态方法:

1
2
3
4
5
6
7
8
class Whatever {
public static varType myVar = initializeClassVariable();
private static varType initializeClassVariable() {
// initialization code goes here
}
}

使用私有静态方法的好处是如果你需要重新初始化变量就可以重用它。

初始化实例成员Initializing Instance Members

通常,你可以经初始化实例成员的代码放进一个构造器,同样还有两个方法可以达到这种目的,初始化代码块,以及final方法;

实例变量初始化代码块域静态代码块类似,不过少了static关键字:

1
2
3
{
// whatever code is needed for initialization goes here
}

Java编译器将初始化代码块复制到每一个构造器,因此使用这种方法的好处是可以在多个构造其中分享同一代码块。

final method final方法 不可以被子类重写。在接口域与继承一颗中我们还会详细讨论。下面是一个使用final方法初始化实例变量的例子:

1
2
3
4
5
6
7
8
class Whatever {
private varType myVar = initializeInstanceVariable();
protected final varType initializeInstanceVariable() {
// initialization code goes here
}
}

特别是当子类想要重用这个初始化方法的时候使用这种方法。使用final方法是因为在实例实例化的过程中调用非final的方法可能引起很多的问题。(思考)

Java 学习指南_学习Java:基础-类-类的成员

发表于 2017-10-20 | 分类于 java

理解类的成员

在本节中,我们讨论使用static关键字来创建属于类的字段和方法,而不是示例的成员。

类变量Class Variables

当一定数量的对象按照同样的类模板创建时,它们每一个都拥有不同的实例变量 的副本。在自行车类Bicycle 中,实例变量为 cadence, gear, 和speed. 没一个自行车 Bicycle 对象的这些变量都有着不同的值,存储在不同的内存位置中。

有时候,你想要有对所有对象都相同的变量。这就要使用static修饰符来完成。声明时带有static修饰符的字段/域叫做 静态域 或者 类变量 (static fields or class variables).

它们是域类相关联的而不是任一个对象。每一个类的实例共享类变量,存储在一个固定的内存位置。任一个对象都可以改变类变量的值,并且类变量也可以在不创建任何类的实例的情况下进行操作。

例如,假设你想创建一定数量的自行 Bicycle对象并且为每一个分配一个序号,第一个对象的开始值为1. 这个ID数字对域每一个对象都都是唯一的因此是一个实例变量。同时,你需要你个字段来跟踪已经创建了多少Bicycle对象,这样你可以直到为下一个对象分配什么ID。这么一个字段并不是域任一个单独的对象关联的,而是对于整个类。这样你就需一个类变量, numberOfBicycles,就像相面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Bicycle {
private int cadence;
private int gear;
private int speed;
// add an instance variable for the object ID
private int id;
// add a class variable for the
// number of Bicycle objects instantiated
private static int numberOfBicycles = 0;
...
}

类变量是通过它自身的类名来引用的,例如

1
Bicycle.numberOfBicycles

这样就可以清晰的直到它们是类变量.


注意: 你同样可以使用一个对象来引用静态域如下

1
myBike.numberOfBicycles

但是并不鼓励这么做,因为这样做并不能清除的表明它们是类变量。


你可以使用 Bicycle 构造器来为每一个实例变量id赋值,并增加类变量 numberOfBicycles 的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Bicycle {
private int cadence;
private int gear;
private int speed;
private int id;
private static int numberOfBicycles = 0;
public Bicycle(int startCadence, int startSpeed, int startGear){
gear = startGear;
cadence = startCadence;
speed = startSpeed;
// increment number of Bicycles
// and assign ID number
id = ++numberOfBicycles;
}
// new method to return the ID instance variable
public int getID() {
return id;
}
...
}

类的方法Class Methods

Java编程语言支持静态变量的同时也支持静态方法。静态方法,在定义时使用static修饰符,应该通过类名类直接调用,而无需创建一个类的实例,例如:

1
ClassName.methodName(args)

注意: 你同样可以使用一个对象来引用静态方法如下

1
instanceName.methodName(args)

但是并不鼓励这么做,因为这样做并不能清除的表明它们是类的方法。


静态方法一个常用的地方使用来方位静态域。例如,我们可以为Bicycle类添加一个静态方法来访问 numberOfBicycles 静态域:

1
2
3
public static int getNumberOfBicycles() {
return numberOfBicycles;
}

并不是所有的静态方法,静态域 与实例变量实例方法的 结合使用都使允许的:

  • 实例方法可以直接访问实例变量与实例方法。
  • 实例方法可以直接调用类变量以及类方法。Instance methods can access class variables and class methods directly.
  • 类方法可以直接方法类变量与类方法。Class methods can access class variables and class methods directly.
  • 类方法不能直接访问实例变量与实例方法——它们必须通过一个对象来引用。同样,类方法不能使用this关键字,因为这里的this不能指向任何实例。

常量Constants

static修饰符,经常与final修饰符结合使用,用来定义常量。final修饰符表明这个字段的值不能改变。

例如,下面的变量声明了一个常量PI,值为pi(圆周率:圆的周长与直径的比值)的近似值:

1
static final double PI = 3.141592653589793;

这种方式定义的常量不能重新赋值,如果你尝试这么做的话就会得到一个编译时错误。按照管理,常量的命名单词用大写字母拼写,如果名字包含了多个单词,使用下划线_分割.


注意:如果一个基本数据类型或者字符串被定义为常量并且值在编译时已经确知,编译器将所有代码中出现的所有常量名替换为它的值。这叫做 编译时常量 compile-time constant. 如果你使用的常量外部世界值发生改变(例如,立法确认pi的值应该为3.975),那么你就需要重新编译所以用到这个常量的类来获取当前固定值。


自行车Bicycle 类

经过所有的改变之后 Bicycle 现在如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class Bicycle {
private int cadence;
private int gear;
private int speed;
private int id;
private static int numberOfBicycles = 0;
public Bicycle(int startCadence,
int startSpeed,
int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
id = ++numberOfBicycles;
}
public int getID() {
return id;
}
public static int getNumberOfBicycles() {
return numberOfBicycles;
}
public int getCadence() {
return cadence;
}
public void setCadence(int newValue) {
cadence = newValue;
}
public int getGear(){
return gear;
}
public void setGear(int newValue) {
gear = newValue;
}
public int getSpeed() {
return speed;
}
public void applyBrake(int decrement) {
speed -= decrement;
}
public void speedUp(int increment) {
speed += increment;
}
}

Java 学习指南_学习Java:基础-类-访问控制

发表于 2017-10-18 | 分类于 java

类成员的访问控制

访问等级限定词确定了其他类是否可以使用一个特定的field字段或者方法.有两种访问等级的控制:

  • 类的访问等级—公有public, 或者包级权限 package-private (没有显式声明).
  • 成员访问等级—公有public,私有 private, 保护protected, or包级权限 package-private (没有显示声明).

一个类的声明可以不包含限定符public,public共有类对于所有类都是可见的。如果一个类没有限定修饰符(默认等级为,包级权限),只对它所在的包内可见。(包命名了一组相关联的类,后面的课程将会学习).

对类的成员的访问等级,你同样可以使用public限定修饰符或者无修饰符(包级权限package-private),无限定修饰符时与类的情况一样并且意义也一样。对于成员来说,仍然有两种额外的访问限定修饰符:private与protected.private限定符表的名该成员私有,只能在本类之内访问。protected修饰符表明该成员可以被同一个包里的类访问(与默认情况,包级权限一样),并且也可以被其他包中该类的子类所访问。

以下表格展示了不同限定修饰符所确定的成员访问权限。

Modifier Class Package Subclass World
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N

第一列数据列展示了类对他资深成员的访问权限。如你所见,一个类总是可以访问它的成员。第二个数据列展示类同一个包内的其他类(不考虑类为其子类的情况)对本类成员的访问权限。第三个数据列展示本类所在包之外的子类对本类成员的访问权限。第四列展示了是否所有的类都对成员具有访问权限。

访问权限通过两种方式起作做。首先,当你使用来自其他资源的类时,例如Java平台的类,访问权限确定了那些类的成员你的类可以调用。第二,当你写一个类时,你需却决定你的每一个类的成员变量或者方法的访问权限。

当我们来看一系列类以及访问权限如何影响可见性。下面的图标展示了本例中的四个类以及它们之间的关系。

Classes and Packages of the Example Used to Illustrate Access Levels

展示访问权限的类与包的图例Classes and Packages of the Example Used to Illustrate Access Levels

以下表格展示了不同限定修饰符下Alpha类的成员对于其他类的可见性。

Modifier Alpha Beta Alphasub Gamma
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N

选择访问权限的提示:

如果其他的程序员使用你的类,你想要确保不因滥用而出现错误。访问权限可以帮助你做这些.

  • 对特定的成员使用最严格的访问权限。使用 private 除非你有更好的理由不这么做。
  • 避免使用public 除非为常量。(本教程的很多例子中都使用了public fields。这么做可以简单的解释一些知识点,但是不推荐在产品的代码中这么做)。公有字段倾向于将你链接想特定的实现并且限定了你改变代码灵活性。

1234…8
Hello Young

Hello Young

80 日志
1 分类
75 标签
GitHub
  • Phaser
© 2018 Hello Young
由 Hexo 强力驱动
主题 - NexT.Pisces