Java 关键字总结

1.abstract

解释:抽象,抽象的
用法:类修饰符的一种,可以用来修饰类,方法,被修饰的类不能够实例化(即不能用** new **关键字得到对象),被修饰的方法不能够实现。一个抽象类中不一定要有抽象方法,但是一个类里面有抽象方法就一定是抽象类。如果由类要继承抽象类的话,必须要实现里面的抽象方法。构造方法和静态方法不可以修饰为抽象。

abstract 关键字可以修改类或方法。

abstract 类可以扩展(增加子类),但不能直接实例化。

abstract 方法不在声明它的类中实现,但必须在某个子类中重写。

-示例-

1
2
3
public abstract class MyClass {
}
public abstract String myMethod();

-注释-
采用 abstract 方法的类本来就是抽象类,并且必须声明为 abstract。
abstract 类不能实例化。
仅当 abstract 类的子类实现其超类的所有 abstract 方法时,才能实例化 abstract 类的子类。这种类称为具体类,以区别于 abstract 类 。
如果 abstract 类的子类没有实现其超类的所有 abstract 方法,该子类也是 abstract 类。
abstract 关键字不能应用于 static、private 或 final 方法,因为这些方法不能被重写,因此,不能在子类中实现。
final 类的方法都不能是 abstract,因为 final 类不能有子类。

  1. abstract 抽象用在类的声明中来指明一个类是不能被实例化的,但是可以被其他类继承。一个抽象类可以使用抽象方法,抽象方法不需要实现,但是需要在子类中被实现。它的用途是向子类提供通用的信息。抽象类可以包含正常的类包含的任何东西,也就是说,类和实例变量,以及带有任何修饰词的方法。只有抽象类可能有抽象方法。如果一个不是抽象的类包含一个抽象方法,那么将会出现编译错误。
    例子:如果下列的一个成立,那么一个有抽象方法:
    a. 它显式的声明了一个
    abstract 方法。
    b. 它从它的直接父类继承了一个抽象方法。
    c. 一个直接的类的父借口声明或继承了它的一个方法(这因此必须是
    abstract
    )
  2. 如果用户声明** private**,static, 和** final 方法为 abstarct,编译错误将出现。不可能重设一个 private 方法,因为一个 abstarct private 永远不能被实现。static 方法总是可用的,因此必须有一个实现;static abstract 将永远没有实现。final 方法不能被重设,因此不回有 final abstract **方法的实现。

2.boolean

解释:数据类型,布尔型,它的取值只能为** true 或者 false。
用法:用来定义临时变量、形参、属性的数据类型。格式如下:
boolean 【变量名、形参名、属性名】**
变量的值可以是 true 或 false。

-示例-

1
2
3
4
boolean valid = true;
if (valid) {
<statement>
}

-注释-
boolean 变量只能以 true 或 false 作为值。
boolean 不能与数字类型相互转换。
包含 boolean 操作数的表达式只能包含 boolean 操作数。
boolean 类是 boolean 原始类型的包装对象类。

3.break

用法:用于提前退出 for、while 或 do 循环,或者在 switch 语句中用来结束 case 块。

-示例-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for (i=0; i<max; i++) {
if (<loop finished early>) {
break;
}
}
int type = <some value>;
switch (type) {
case 1:
<statement>
break;
case 2:
<statement>
break;
default:
<statement>
}

-注释-
break 总是退出最深层的 while、for、do 或 switch 语句。

4.byte

解释:数据类型,字节型
用法:用来定义临时变量、形参、属性的数据类型。格式如下:byte 【变量名、形参名、属性名】

byte 是 Java 原始类型。
byte 可存储在 [-128, 127] 范围以内的整数值。

-示例-

1
byte b = 124;

-注释-
byte 类是 byte 原始类型的包装对象类。它定义代表此类型的值的范围的 **MIN_VALUE **和 **MAX_VALUE **常量。
Java 中的所有整数值都是 32 位的 int 值,除非值后面有 l 或 L(如 235L),这表示该值应解释为 long。

5.case

用法:用来标记 switch 语句中的每个分支。

-示例-

1
2
3
4
5
6
7
8
9
10
11
12
int arg = <some value>;
switch (arg) {
case 1:
<statements>
break;
case 2:
<statements>
break;
default:
<statements>
break;
}

-注释-
case 块没有隐式结束点。break 语句通常在每个 case 块末尾使用,用于退出 switch 语句。
如果没有 break 语句,执行流将进入所有后面的 case 和/或 default 块。

6.catch

解释:捕捉,捕获
用法:当发现被监视的代码中有异常出现的时候,**catch 就用相对应的异常类进行捕捉。并且打印出相应的异常信息。catch 块,必须要配合 try 块一起使用,不能单独使用,也不能直接和 finally **块一起使用。
catch 关键字用来在 try-catch 或 try-catch-finally 语句中定义异常处理块。

-示例-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
try {
<可能引发异常的块>
} catch (<java.lang.Exception 或子类> e) {
<处理异常 e 的代码>
}
try {
<可能引发其他异常的块>
} catch (FooException e) {
<处理 FooException e 的代码>
} catch (BarException e) {
<处理 BarException e 的代码>
}
try {
<可能引发异常的块>
} catch (<java.lang.Exception 或子类> e) {
<处理异常 e 的代码>
}
finally {
<有异常或无异常情况下都执行的语句>
}

-注释-
开始和结束标记 ** {** 和 } 是 catch 子句语法的一部分,即使该子句只包含一个语句,也不能省略这两个标记。
每个 try 块都必须至少有一个 catch 或 finally 子句。
如果某个特定异常类未被任何 catch 子句处理,该异常将沿着调用栈递归地传播到下一个封闭 try 块。如果任何封闭 try 块都未捕获到异常,Java >解释器将退出,并显示错误消息和堆栈跟踪信息。

7.char

用法:char 是 Java 原始类型。
char 变量可以存储一个** Unicode** 字符。

-示例-

1
char delimiter = ';';

-注释-
可以使用下列 char 常量:

1
2
3
4
5
6
7
8
9
10
\b - 空格
\f - 换页
\n - 换行
\r - 回车
\t - 水平制表符
\' - 单引号
\" - 双引号
\" - 反斜杠
\xxx - 采用 xxx 编码的 Latin-1 字符。\x 和 \xx 均为合法形式,但可能引起混淆。
\uxxxx - 采用十六进制编码 xxxx 的 Unicode 字符。

Character 类包含一些可用来处理** char 变量的 static **方法,这些方法包括 isDigit()、isLetter()、isWhitespace() 和 **toUpperCase()**。
**char **值没有符号。

8.class

解释:类,种类
用法:定义一个类,这时候定义的类,在内存空间里面没有分配空间。换句话说,它还没有存在于现实生活中,它不是一个实体。类里面有方法有属性,要想访问它里面的方法和属性就要通过对象去使用。
格式如下:

1
访问权限修饰符 class 类名{}

class 关键字用来声明新的 Java 类,该类是相关变量和/或方法的集合。
类是面向对象的程序设计方法的基本构造单位。类通常代表某种实际实体,如几何形状或人。类是对象的模板。每个对象都是类的一个实例。
要使用类,通常使用 new 操作符将类的对象实例化,然后调用类的方法来访问类的功能。

-示例-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Rectangle{
float width;
float height;
public Rectangle(float w, float h){
width = w;
height = h;
}
public float getWidth(){
return width;
}
public float getHeight(){
return height;
}
}

9.continue

用法:continue 关键字用来跳转到 for、while 或 do 循环的下一个迭代。

-示例-

1
2
3
4
5
6
7
for (i=0; i<max; i++){
<statements>
if (<done with this iteration>){
continue;
}
<statements>
}

-注释-
continue 总是跳到最深层 while、for 或 do 语句的下一个迭代。

10.default

用法:default 关键字用来标记 **switch **语句中的默认分支。

-示例-

1
2
3
4
5
6
7
8
9
10
11
12
int arg = <some value>;
switch (arg){
case 1:
<statements>
break;
case 2:
<statements>
break;
default:
<statements>
break;
}

-注释-
default 块没有隐式结束点。break 语句通常在每个 case 或 default 块的末尾使用,以便在完成块时退出 **switch 语句。
如果没有 default 语句,其参数与任何 case 块都不匹配的 switch
语句将不执行任何操作。

11.do

用法:do 关键字用于指定一个在每次迭代结束时检查其条件的循环。

-示例-

1
2
3
4
do{
<statements>
}
while (!found);

-注释-
do 循环体至少执行一次。
条件表达式后面必须有分号。

12.double

用法:double 是 Java 原始类型。double 变量可以存储双精度浮点值。

-示例-

1
2
3
4
double ratio = .01;
double diameter = 6.15;
double height = 1.35E03; // 1.35 * 103 或 1350.0
double height = 1e-2; // 1.0 * 10-2 或 0.01

-注释-
由于浮点数据类型是实际数值的近似值,因此,一般不要对浮点数值进行是否相等的比较。
Java 浮点数值可代表无穷大和 NaN(非数值)。Double 包装对象类用来定义常量 MIN_VALUE、MAX_VALUE、NEGATIVE_INFINITY、POSITIVE_INFINITY 和 NaN。

13.else

用法:else 关键字总是在 if-else 语句中与 if 关键字结合使用。else 子句是可选的,如果 if 条件为 false,则执行该子句。

-示例-

1
2
3
4
5
6
if (condition){
<statements>
}
else{
<statements>
}

14.extends

解释:扩充,扩展
用法:一个类继承另一个类的关键字,当出现了这个关键字,两个类就有了继承关系,**extends 前面的类,我们称之为父类。extends **后面的类,我们称之为子类。有了继承关系之后,我们就说子类拥有父类继承下来的所有非私有的成员(包括了属性,方法),但是不包括构造方法
extends 关键字用在 class 或 interface 声明中,用于指示所声明的类或接口是其名称后跟有 extends 关键字的类或接口的子类。

-示例-

1
2
public class Rectangle extends Polygon{
}

-注释-
在上例中,Rectangle 类继承 Polygon 类的所有 public 和 protected 变量和方法。
Rectangle 类可以重写 Polygon 类的任何非 final 方法。
一个类只能扩展一个其他类。

15.false

解释:假,布尔型的字面值
用法:**boolean **类型的取值范围之一。
false 关键字代表 boolean 变量的两个合法值之一。

-示例-

1
boolean isComplete = false;

16.final

解释:最终的,决定性的
用法:修饰符的一种,它可以用来修饰类,方法,属性。当一个属性被修饰成** final 的之后,这个属性变成了常量,它的值必须在定义的时候初始化,并且后面的代码不能对其进行修改,它的命名应该全部都是大写。当一个方法被修饰成 final **的之后,这个方法在继承中是不能够被进行覆盖的。当一个类被修饰成 final 的之后,这个类不能再有子类。
final 关键字可以应用于类,以指示不能扩展该类(不能有子类)。
final 关键字可以应用于方法,以指示不能重写任何子类中的方法。

-示例-

1
2
3
4
5
6
7
8
public final class MyFinalClass{
}
public class MyClass{
public final String myFinalMethod()
{
<statements>
}
}

例子:

1
2
3
4
5
6
public final class Math{
private final float PI = 3.1415926;
public final int abs(int i){
return i>=0?i:-I;
}
}

-注释-
一个类不能同时是 abstract 又是 final。abstract 意味着必须扩展类,final 意味着不能扩展类。
一个方法不能同时是 abstract 又是** final。abstract** 意味着必须重写方法,final 意味着不能重写方法。

  1. 对于基本类型前加以** final 修饰,表示被修饰的变量为常数,不可以修改。一个既是 static 又是 final **的字段表示只占据一段不能改变的存储空间。
  2. **final 用于对象应用时,final **使应用恒定不变。一旦引用被初始化指向一个对象,就无法再把它指向另一个对象。
  3. final 方法:一是把方法锁定,以防止继承类修改它的含义,二是确保继承中使方法行为保持不变,并且不会被覆盖。类中所有的 private 方法都隐式地指定为是 final。
  4. **final **参数:对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值,也就是说你可以在方法中更改这个参数变量而不会影响到调用语句,然而对于对象变量,却显得很实用,以为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句的对象变量,当你在方法中不需要改变作为参数的变量时,明确使用 final 进行声明,会防止你无意的修改而影响到调用方法。
  5. final 类:当将某个类的整体定义为 final 时,就表明了该类不允许被继承。

17.finally

解释:最后,终于,不可更改地
用法:在异常处理机制当中,它的作用就像是人吃饭一样,必须得做的,不论有异常还是没有异常都要执行的代码就可以放到** finally 块当中去。finally 块,必须要配合 try 块一起使用,不能单独使用,也不能直接和 catch **块一起使用。
finally 关键字用来定义始终在 try-catch-finally 语句中执行的块。
finally 块通常包含清理代码,用在部分执行 try 块后恢复正常运行。

-示例-

1
2
3
4
5
6
7
8
9
try{
<可能引发异常的块>
}
catch (<java.lang.Exception 或子类> e){
<处理异常 e 的代码>
}
finally{
<有异常或无异常情况下都执行的语句>
}

-注释-
开始和结束标记 { 和 } 是 finally 子句语法的一部分,即使该子句只包含一个语句,也不能省略这两个标记。
每个 try 块都必须至少有一个 catch 或 finally 子句。
如果执行 try 块的任何部分,不论是否出现异常,也不论 try 或 catch 块是否包含 return、continue 或 break 语句,都一定会执行 finally 块中的代码。
如果不出现异常,控件将跳过 try 块,进入 finally 块。
如果在执行 try 块期间出现异常,并且相应的 catch 块包含 break、continue 或 return 语句,控件将首先穿过 finally 块,之后再执行 break、continue 或 return。

18.float

用法:float 是 Java 原始类型。float 变量可以存储单精度浮点值。

-示例-

1
2
3
4
float ratio = .01;
float diameter = 6.15;
float height = 1.35E03; // 1.35 * 103 或 1350.0
float height = 1e-2; // 1.0 * 10-2 或 0.01

-注释-
使用此关键字时应遵循下列规则:
Java 中的浮点文字始终默认为双精度。要指定单精度文字值,应在数值后加上 f 或 F,如 0.01f。
由于浮点数据类型是实际数值的近似值,因此,一般不要对浮点数值进行是否相等的比较。
Java 浮点数值可代表无穷大和 NaN(非数值)。Float 包装对象类用来定义常量** MIN_VALUE、MAX_VALUE、NEGATIVE_INFINITY、POSITIVE_INFINITY** 和 NaN。

19.for

用法:for 关键字用于指定一个在每次迭代结束前检查其条件的循环。

-示例-

1
2
3
4
int i;
for (i=0; i<max; i++){
<statements>
}

-注释-
for 语句的形式为 for(initialize; condition; increment)
控件流进入 for 语句时,将执行一次 initialize 语句。
每次执行循环体之前将计算 condition 的结果。如果 condition 为 true,则执行循环体。
每次执行循环体之后,在计算下一个迭代的 condition 之前,将执行 increment 语句。

20.if

用法:if 关键字指示有条件地执行代码块。条件的计算结果必须是布尔值。

-示例-

1
2
3
4
5
6
7
8
9
if (condition){
<statements>
}
if (condition){
<statements>
}
else{
<statements>
}

-注释-
if 语句可以有可选的 else 子句,该子句包含条件为 false 时将执行的代码。
包含 boolean 操作数的表达式只能包含 boolean 操作数。

21.implements

解释:实现
用法:用来让一个类实现一个接口的关键字,实现接口的这个类必须实现接口里面所有的方法。
implements 关键字在 class 声明中使用,以指示所声明的类提供了在 **implements *关键字后面的名称所指定的接口中所声明的所有方法的实现。

-示例-

1
2
public class Truck implements IVehicle{
}

-注释-
在上例中,Truck 类必须提供在 IVehicle 接口中所声明的所有方法的实现。
否则,Truck 类将是独立的;它可以声明其他方法和变量,并扩展另一个类。
一个类可以实现多个接口。

22.import

解释:引入,导入
用法:相对应打包而言,当一个类被放在包下面了,如果要使用这个类就必须到这个包下面去寻找这个类,因此出现了导包语句。导包可以有多条语句。
import 关键字使一个包中的一个或所有类在当前 Java 源文件中可见。可以不使用完全限定的类名来引用导入的类。

-示例-

1
2
import java.io.File;
import java.net.*;

-注释-
当多个包包含同名的类时,许多 Java 程序员只使用特定的 import 语句(没有“*”)来避免不确定性。

23.instanceof

用法:instanceof 关键字用来确定对象所属的类。

-示例-

1
2
3
if (node instanceof TreeNode){
<statements>
}

-注释-
在上例中,如果 node 是 TreeNode 类的实例,或者是 TreeNode 的子类的实例,则 instanceof 表达式的值将为 true。
**instanceof **通常是用于判断父类或者接口的引用是否是某个子类的实例,例如:

1
2
3
4
5
6
class Animal{}
class Bird extends Animal {}
class Dog extends Animal {}
Animal a= new Bird();
System.out.println( a instanceof Bird);
System.out.println( a instanceof Dog);

24.int

用法:int 是 Java 原始类型。
int 变量可以存储 32 位的整数值。

-示例-

1
2
3
int number = 5;
int octalNumber = 0377;
int hexNumber = 0xff;

-注释-
Integer 类是 int 原始类型的包装对象类。它定义代表此类型的值的范围的 MIN_VALUE 和 MAX_VALUE 常量。
Java 中的所有整数值都是 32 位的 int 值,除非值后面有 l 或 L(如 235L),这表示该值应解释为 long。

25.interface

解释:接口,界面
用法:它本质上是一个类,一个完全抽象的类,里面没有任何实现的方法。它不是用来继承的,是用来实现的。某个类如果实现了接口就必须要实现接口里面的所有方法。并且接口是不能用来实例化的,它也是不能通过** new **关键字获得对象。
interface 关键字用来声明新的 Java 接口,接口是方法的集合。
接口是 Java 语言的一项强大功能。任何类都可声明它实现一个或多个接口,这意味着它实现了在这些接口中所定义的所有方法。

-示例-

1
2
3
4
5
public interface IPolygon{
public float getArea();
public int getNumberOfSides();
public int getCircumference();
}

-注释-
实现了接口的任何类都必须提供在该接口中的所有方法的实现。
一个类可以实现多个接口。

26.long

用法:long 是 Java 原始类型。**long **变量可以存储 64 位的带符号整数。

-示例-

1
2
3
4
long number = 5;
long anotherNumber = 34590L;
long octalNumber = 0377;
long hexNumber = 0xffl;

-注释-
Long 类是 long 原始类型的包装对象类。它定义代表此类型的值的范围的 MIN_VALUE 和 MAX_VALUE 常量。
Java 中的所有整数值都是 32 位的 int 值,除非值后面有 l 或 L(如 235L),这表示该值应解释为** long**。

27.native

说明:native 关键字可以应用于方法,以指示该方法是用 Java 以外的语言实现的。

-示例-

1
native String getProcessorType();

-注释-
Java 不是完美的,Java 的不足除了体现在运行速度上要比传统的 C++慢许多之外,Java 无法直接访问到操作系统底层(如系统硬件等),为此 Java 使用** native 方法来扩展 Java 程序的功能。
可以将 native 方法比作 Java 程序同C程序的接口,其实现步骤:
1. 在 Java 中声明
native() 方法,然后编译;
2. 用 javah 产生一个
.h 文件;
3. 写一个。cpp 文件实现 native 导出方法,其中需要包含第二步产生的
.h 文件(注意其中又包含了 JDK 带的 jni.h 文件);
4. 将第三步的。cpp 文件编译成动态链接库文件;
5. 在 Java 中用
System.loadLibrary() 方法加载第四步产生的动态链接库文件,这个 native() **方法就可以在 Java 中被访问了。

JAVA 本地方法适用的情况

  1. 为了使用底层的主机平台的某个特性,而这个特性不能通过 JAVA API 访问
  2. 为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用 JAVA 编写的
  3. 为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。
    首先写好 JAVA 文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /*
    * Created on 2005-12-19 Author shaoqi
    */
    package com.hode.hodeframework.modelupdate;
    public class CheckFile
    {
      public native void displayHelloWorld();
      static
      {
       System.loadLibrary("test");
      }
      public static void main(String[] args) {
       new CheckFile().displayHelloWorld();
      }
    }
    然后根据写好的文件编译成** CLASS 文件
    然后在 classes 或 bin 之类的
    class 根目录下执行 javah -jni com.hode.hodeframework.modelupdate.CheckFile, 就会在根目录下得到一个 com_hode_hodeframework_modelupdate_CheckFile.h 的文件,然后根据头文件的内容编写 com_hode_hodeframework_modelupdate_CheckFile.c **文件
    1
    2
    3
    4
    5
    6
    7
    #include "CheckFile.h"
    #include
    JNIEXPORT void JNICALL Java_com_hode_hodeframework_modelupdate_CheckFile_displayHelloWorld(JNIEnv *env, jobject obj)
    {
    printf("Hello world!\n");
      return;
    }
    之后编译生成 DLL 文件如“test.dll”,名称与** System.loadLibrary(“test”) 中的名称一致
    vc 的编译方法:cl -I%java_home%\include -I%java_home%\include\win32 -LD com_hode_hodeframework_modelupdate_CheckFile.c -Fetest.dll
    最后在运行时加参数
    -Djava.library.path=[dll 存放的路径]**

28.new

解释:新建
用法:创建对象时使用,当一看倒** new **关键字,意味着在内存的堆空间里开辟了一个空间,存放了相对应类型的对象出来。创建的对象由一个相对应类型的引用去指向访问,类似与 C 语言中指针的用法。
格式如下:类名 引用名 = new 类名 ()。
new 关键字用于创建类的新实例。

-示例-

1
2
String sName = new String();
Float fVal = new Float(0.15);

-注释-
new 关键字后面的参数必须是类名,并且类名的后面必须是一组构造方法参数(必须带括号)。
参数集合必须与类的构造方法的签名匹配。
= 左侧的变量的类型必须与要实例化的类或接口具有赋值兼容关系。

29.null

解释:空
用法:对象的一种取值,称呼为:“null 值”,这时候我们理解成内存里面还没有得到这个对象空间。
null 是 Java 的保留字,表示无值。

-示例-

1
2
3
4
5
6
Integer i;
i = null;
String s;
if (s != null){
<statements>
}

-注释-
将 null 赋给非原始变量相当于释放该变量先前所引用的对象。
不能将 null 赋给原始类型(byte、short、int、long、char、float、double、boolean)变量。

30.package

解释:包裹,包
用法:为了防止同一个目录下面存在于两个相同名字的源文件,使用打包的用处和文件夹的作用是相似的。要注意,打包语句一定只能放在源文件的第一句话,并且不能在同一个源文件中出现两个或者两个以上的打包语句。包可以嵌套,通常来讲,第一个包称之为根包,后面的嵌套包称之为子包。
package 关键字指定在 Java 源文件中声明的类所驻留的 Java 包。

-示例-

1
2
3
package com.mycompany;
public class MyClass{
}

-注释-
package 语句(如果出现)必须是 Java 源文件中的第一个非-注释-性文本。
在上面的**-示例-中,MyClass** 类的完全限定类名是 com.mycompany.MyClass。
如果 Java 源文件不包含 package 语句,在该文件中定义的类将位于“默认包”中。请注意,不能从非默认包中的类引用默认包中的类。

31.private

解释:访问权限修饰符的一种,私有的意思。
用法:用来修饰属性,方法。只能类的内部本身才能够访问,不能修饰类。
private 关键字是访问控制修饰符,可以应用于方法或字段(在类中声明的变量)。

-示例-

1
2
3
4
5
6
7
8
public class MyPublicClass{
private class MyPrivateClass{
}
private int i;
private String myMethod(){
<statements>
}
}

-注释-
只能在声明 private(内部)类、方法或字段的类中引用这些类、方法或字段。在类的外部或者对于子类而言,它们是不可见的。
所有类成员的默认访问范围都是 package 访问,也就是说,除非存在特定的访问控制修饰符,否则,可以从同一个包中的任何类访问类成员。

32.protected

解释:访问权限修饰符的一种,受保护的意思。
用法:用来修饰属性,方法。只能符合有包内友好关系,或者是父子友好的类才能够访问,不能修饰类。
protected 关键字是可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符。

-示例-

1
2
3
4
5
6
7
8
public class MyPublicClass{
protected class MyPrivateClass{
}
protected int i;
protected String myMethod(){
<statements>
}
}

-注释-
可以在声明 protected 类、方法或字段的类、同一个包中的其他任何类以及任何子类(无论子类是在哪个包中声明的)中引用这些类、方法或字段。
所有类成员的默认访问范围都是 package 访问,也就是说,除非存在特定的访问控制修饰符,否则,可以从同一个包中的任何类访问类成员。

33.public

解释:访问权限修饰符的一种,公开,公共的意思。
用法:用来修饰类,属性,方法。对于外部的任何类都能够访问。
public 关键字是可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符。

-示例-

1
2
3
4
5
6
7
8
public class MyPublicClass{
public class MyPrivateClass{
}
public int i;
public String myMethod(){
<statements>
}
}

-注释-
可能只会在其他任何类或包中引用 public 类、方法或字段。
所有类成员的默认访问范围都是 package 访问,也就是说,除非存在特定的访问控制修饰符,否则,可以从同一个包中的任何类访问类成员。

34.return

用法:return 关键字会导致方法返回到调用它的方法,从而传递与返回方法的返回类型匹配的值。

-示例-

1
2
3
4
5
6
7
8
9
10
11
12
public void myVoidMethod(){
<statements>
return;
}
public String myStringMethod(){
String s = "my response";
return s;
}
public int myIntMethod(){
int i = 5;
return(i);
}

-注释-
如果方法具有非 void 的返回类型,return 语句必须具有相同或兼容类型的参数。
返回值两侧的括号是可选的。

35.short

用法:short 是 Java 原始类型。short 变量可以存储 16 位带符号的整数。

-示例-

1
2
3
short number = 5;
short octalNumber = 0077;
short hexNumber = 0xff;

-注释-
Short 类是 short 原始类型的包装对象类。它定义代表此类型的值的范围的 MIN_VALUE 和 MAX_VALUE 常量。
Java 中的所有整数值都是 32 位的 int 值,除非值后面有 l 或 L(如 235L),这表示该值应解释为 long。

36.static

解释:静态的
用法:修饰符的一种,能够用来修饰属性和方法。需要注意的是被修饰的属性和方法,不再属于对象所有,而是属于类,意味着,要访问这些属性和方法不再通过对象而是直接用类名来访问。另外,静态的方法不能够访问非静态属性,非静态的方法能够访问静态的属性。
static 关键字可以应用于内部类(在另一个类中定义的类)、方法或字段(类的成员变量)。

-示例-

1
2
3
4
5
6
7
8
public class MyPublicClass{
public final static int MAX_OBJECTS = 100;
static int _numObjects = 0;
static class MyStaticClass{
}
static int getNumObjects(){
}
}

-注释-
通常,static 关键字意味着应用它的实体在声明该实体的类的任何特定实例外部可用。
static(内部)类可以被其他类实例化和引用(即使它是顶级类)。在上面的**-示例-**中,另一个类中的代码可以实例化 MyStaticClass 类,方法是用包含它的类名来限定其名称,如 MyClass.MyStaticClass。
static 字段(类的成员变量)在类的所有实例中只存在一次。
可以从类的外部调用 static 方法,而不用首先实例化该类。这样的引用始终包括类名作为方法调用的限定符。在上面的示例中,
MyClass
类外部的代码以 MyClass.getNumObjects() 的形式调用 getNumObjects() static 方法。

模式:

1
public final static <type> varName = <value>;

通常用于声明可以在类的外部使用的类常量。在引用这样的类常量时需要用类名加以限定。在上面的**-示例-**中,另一个类可以用 MyClass.MAX_OBJECTS 形式来引用 MAX_OBJECTS 常量。

  1. 通常在一个类中定义一个方法为** static**,就是说无须本类的对象就可以直接调用。
  2. 静态变量和静态方法类似。所有此类实例共享此静态变量,也就是说类装载时,只分配一块存储空间,所有此类的对象都可以操控此块存储空间,当然对于 final 就另当别论了。
  3. static 定义的变量会优先于任何其他非** static **变量,不论其出现顺序如何。
  4. **static{}着是用来显式的静态变量初始化,这段代码只会初始化一次,且在类被第一次装载时。
    5>在涉及到继承的时候,会先初始化父类的
    static **变量,然后是子类的。
    6>通常一个普通类不允许声明为静态的,只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用,而不需要实例一个外部类。

37.super

解释:上一级的,超级的
用法:代表了当前类中父类的对象。能够去调用父类的构造方法,属性,方法。但是需要注意,如果是在当前类的构造方法中去调用父类的构造方法,必须把调用语句写在本类构造方法的第一句上。
super 关键字用于引用使用该关键字的类的超类。

-示例-

1
2
3
4
5
6
7
8
public class MyClass{
public MyClass(String arg){
super(arg);
}
public String myStringMethod(){
return super.otherStringMethod();
}
}

-注释-
作为独立语句出现的 super 表示调用超类的构造方法。

1
super.<methodName>()

表示调用超类的方法。只有在如下情况中才需要采用这种用法:要调用在该类中被重写的方法,以便指定应当调用在超类中的该方法。

  1. 在你的方法中的某个刑参名与当前对象的某个成员有相同的名字,这时为了不至于混淆,你便要明确使用** this 关键字来指明你要使用某个成员,使用方法是 this. 成员名,而不带 this 的那个便是形参。另外,还可以用 this. 方法名来引用当前对象的某个方法,但这时 this **就不是必须的了,你可以直接用方法名来访问那个方法。
  2. 当父类的方法被重写后可以用** super 来调用父类的方法。如果父类的方法可以被子类调用,那你可以象使用 this 一样使用它,用 super. 父类中的成员名**的方式来调用。
  3. **super 和 this 直接在其后跟上适当的参数,因此它的意义就有了变化。super 后加参数的是用来调用父类中具有相同形式的构造函数,this **后加参数则调用的是当前具有相同参数的构造函数。
  4. **this 通常指代当前对象,super **通常指带父类。

38.switch

用法:switch 语句用于基于某个表达式选择执行多个代码块中的某一个。

-示例-

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
int arg = <some value>;
switch (arg){
case 1:
<statements>
break;
case 2:
<statements>
break;
default:
<statements>
break;
}
char arg = <some value>;
switch (arg){
case 'y':
case 'Y':
<statements>
break;
case 'n':
case 'N':
<statements>
break;
default:
<statements>
break;
}

-注释-
switch 条件的计算结果必须等于 byte、char、short 或 int。
case 块没有隐式结束点。break 语句通常在每个 case 块末尾使用,用于退出 switch 语句。
如果没有 break 语句,执行流将进入所有后面的 case 和**/**或 default 块。

39.synchronized

用法:synchronized 关键字可以应用于方法或语句块,并为一次只应由一个线程执行的关键代码段提供保护。当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码

-示例-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyClass{
public synchronized static String mySyncStaticMethod(){
}
public synchronized String mySyncMethod(){
}
}
public class MyOtherClass{
Object someObj;
public String myMethod(){
<statements>
synchronized (someObj){
<statements affecting someObj>
}
}
}

-注释-
synchronized 关键字可防止代码的关键代码段一次被多个线程执行。
如果应用于静态方法(如上例中的 MySyncStaticMethod),那么,当该方法一次由一个线程执行时,整个类将被锁定。
如果应用于实例方法(如上例中的 MySyncMethod),那么,当该方法一次由一个线程访问时,该实例将被锁定。
如果应用于对象或数组,当关联的代码块一次由一个线程执行时,对象或数组将被锁定。
synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。

  1. synchronized 方法:通过在方法声明中加入 **synchronized **关键字来声明 **synchronized **方法。
    如:  
    1
    public synchronized void accessVal(int newVal);
    synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
     在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized,以控制其对类的静态成员变量的访问。
    synchronized 方法的缺陷:若将一个大的方法声明为** synchronized** 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。
  2. synchronized 块:通过 synchronized 关键字来声明 synchronized 块。语法如下:
    1
    2
    3
    synchronized(syncObject) {
      //允许访问控制的代码
    }
    synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
    对** synchronized(this) 的一些理解
    一、当两个并发线程访问同一个对象
    object 中的这个 synchronized(this) 同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。package ths;**
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Thread1 implements Runnable {
    public void run() {
    synchronized(this) {
    for (int i = 0; i < 5; i++) {
    System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
    }
    }
    }
    public static void main(String[] args) {
    Thread1 t1 = new Thread1();
    Thread ta = new Thread(t1, "A");
    Thread tb = new Thread(t1, "B");
    ta.start();
    tb.start();
    }
    }
    结果:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    A synchronized loop 0
    A synchronized loop 1
    A synchronized loop 2
    A synchronized loop 3
    A synchronized loop 4
    B synchronized loop 0
    B synchronized loop 1
    B synchronized loop 2
    B synchronized loop 3
    B synchronized loop 4
    二、然而,当一个线程访问 object 的一个** synchronized(this) 同步代码块时,另一个线程仍然可以访问该 object 中的非 synchronized(this) **同步代码块。
    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
    package ths;
    public class Thread2 {
    public void m4t1() {
    synchronized(this) {
    int i = 5;
    while( i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : " + i);
    try {
    Thread.sleep(500);
    } catch (InterruptedException ie) {
    }
    }
    }
    }
    public void m4t2() {
    int i = 5;
    while( i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : " + i);
    try {
    Thread.sleep(500);
    } catch (InterruptedException ie) {
    }
    }
    }
    public static void main(String[] args) {
    final Thread2 myt2 = new Thread2();
    Thread t1 = new Thread( new Runnable() { public void run() { myt2.m4t1(); } }, "t1" );
    Thread t2 = new Thread( new Runnable() { public void run() { myt2.m4t2(); } }, "t2" );
    t1.start();
    t2.start();
    }
    }
    结果:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    t1 : 4
    t2 : 4
    t1 : 3
    t2 : 3
    t1 : 2
    t2 : 2
    t1 : 1
    t2 : 1
    t1 : 0
    t2 : 0
    三、尤其关键的是,当一个线程访问** object 的一个 synchronized(this) 同步代码块时,其他线程对 object 中所有其它 synchronized(this) **同步代码块的访问将被阻塞。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //修改 Thread2.m4t2() 方法:
    public void m4t2() {
    synchronized(this) {
    int i = 5;
    while( i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : " + i);
    try {
    Thread.sleep(500);
    } catch (InterruptedException ie) {
    }
    }
    }
    }
    结果:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    t1 : 4
    t1 : 3
    t1 : 2
    t1 : 1
    t1 : 0
    t2 : 4
    t2 : 3
    t2 : 2
    t2 : 1
    t2 : 0
    四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问** object 的一个 synchronized(this) 同步代码块时,它就获得了这个 object 的对象锁。结果,其它线程对该 object **对象所有同步代码部分的访问都被暂时阻塞。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //修改 Thread2.m4t2() 方法如下:
    public synchronized void m4t2() {
    int i = 5;
    while( i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : " + i);
    try {
    Thread.sleep(500);
    } catch (InterruptedException ie) {
    }
    }
    }
    结果:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    t1 : 4
    t1 : 3
    t1 : 2
    t1 : 1
    t1 : 0
    t2 : 4
    t2 : 3
    t2 : 2
    t2 : 1
    t2 : 0
    五、以上规则对其它对象锁同样适用。
    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
    package ths;

    public class Thread3 {
    class Inner {
    private void m4t1() {
    int i = 5;
    while(i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i);
    try {
    Thread.sleep(500);
    } catch(InterruptedException ie) {
    }
    }
    }
    private void m4t2() {
    int i = 5;
    while(i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
    try {
    Thread.sleep(500);
    } catch(InterruptedException ie) {
    }
    }
    }
    }
    private void m4t1(Inner inner) {
    synchronized(inner) { //使用对象锁
    inner.m4t1();
    }

    private void m4t2(Inner inner) {
    inner.m4t2();
    }

    public static void main(String[] args) {
    final Thread3 myt3 = new Thread3();
    final Inner inner = myt3.new Inner();
    Thread t1 = new Thread( new Runnable() {public void run() { myt3.m4t1(inner);} }, "t1");
    Thread t2 = new Thread( new Runnable() {public void run() { myt3.m4t2(inner);} }, "t2");
    t1.start();
    t2.start();
    }
    }
    结果:尽管线程 t1 获得了对 Inner 的对象锁,但由于线程 t2 访问的是同一个 Inner 中的非同步部分。所以两个线程互不干扰。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    t1 : Inner.m4t1()=4
    t2 : Inner.m4t2()=4
    t1 : Inner.m4t1()=3
    t2 : Inner.m4t2()=3
    t1 : Inner.m4t1()=2
    t2 : Inner.m4t2()=2
    t1 : Inner.m4t1()=1
    t2 : Inner.m4t2()=1
    t1 : Inner.m4t1()=0
    t2 : Inner.m4t2()=0
    现在在** Inner.m4t2() 前面加上 synchronized**:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private synchronized void m4t2() {
    int i = 5;
    while(i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
    try {
    Thread.sleep(500);
    } catch(InterruptedException ie) {
    }
    }
    }
    结果:
    尽管线程 t1 与 t2 访问了同一个 Inner 对象中两个毫不相关的部分,但因为 t1 先获得了对 Inner 的对象锁,所以 t2 对** Inner.m4t2() **的访问也被阻塞,因为 m4t2() 是 Inner 中的一个同步方法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    t1 : Inner.m4t1()=4
    t1 : Inner.m4t1()=3
    t1 : Inner.m4t1()=2
    t1 : Inner.m4t1()=1
    t1 : Inner.m4t1()=0
    t2 : Inner.m4t2()=4
    t2 : Inner.m4t2()=3
    t2 : Inner.m4t2()=2
    t2 : Inner.m4t2()=1
    t2 : Inner.m4t2()=0

40.this

解释:这,这个
用法:指离它最近类的当前对象,他能够调用本类中的属性,方法,构造方法。需要注意的是在静态方法中不能使用** this **关键字。
this 关键字用于引用当前实例。

-示例-

1
2
3
4
5
public class MyClass{
int number;
public MyClass(int number){
this.number = number;
}

-注释-
当引用可能不明确时,可以使用 this 关键字来引用当前的实例。
在上面的**-示例-中,构造方法参数 number 与类的成员变量同名。this.number** 明确表示 MyClass 的该实例的 number 成员变量。

  1. 在你的方法中的某个刑参名与当前对象的某个成员有相同的名字,这时为了不至于混淆,你便要明确使用 this 关键字来指明你要使用某个成员,使用方法是 this. 成员名,而不带 this 的那个便是刑参。另外,还可以用 this. 方法名来引用当前对象的某个方法,但这时** this **就不是必须的了,你可以直接用方法名来访问那个方法。
  2. 当父类的方法被重写后可以用** super 来调用父类的方法。如果父类的方法可以被子类调用,那你可以象使用 this 一样使用它,用 super. 父类中的成员名**的方式来调用。
  3. **super 和 this 直接在其后跟上适当的参数,因此它的意义就有了变化。super 后加参数的是用来调用父类中具有相同形式的构造函数,this **后加参数则调用的是当前具有相同参数的构造函数。
  4. **this 通常指代当前对象,super **通常指带父类。

41.throw

解释:抛出,扔
用法:用户自己人为的抛出一个异常。可以让本没有异常的代码中出现一个人造的异常出来。既然自己抛出一个异常那就要自己负责到底,自己用 catch 块去将这个异常给捕捉。其语法格式如下:throw 异常对象。

throw 关键字用于引发异常。
-示例-

1
2
3
4
5
6
7
8
9
import java.io.IOException;
public class MyClass{
public method readFile(String filename) throws IOException{
<statements>
if (error){
throw new IOException("error reading file");
}
}
}

-注释-
throw 语句将 java.lang.Throwable 作为参数。Throwable 在调用栈中向上传播,直到被适当的 catch 块捕获。
引发非 RuntimeException 异常的任何方法还必须在方法声明中使用 throws 修饰符来声明它引发的异常。

42.throws

解释:抛出,扔
用法:将方法体内产生的异常,自己不想再去负责捕捉,于是使用 throws 关键字将异常抛出方法体外部去。注意体内产生了什么类型的异常就应该抛出什么样的异常。并且抛出去之后总有一个人要负起这个责任,也就是谁调用了这个方法,谁去捕捉。
throws 关键字可以应用于方法,以便指出方法引发了特定类型的异常。

-示例-

1
2
3
4
5
6
7
8
9
import java.io.IOException;
public class MyClass{
public method readFile(String filename) throws IOException{
<statements>
if (error){
throw new IOException("error reading file");
}
}
}

-注释-
throws 关键字将逗号分隔的 java.lang.Throwables 列表作为参数。
引发非 RuntimeException 异常的任何方法还必须在方法声明中使用 throws 修饰符来声明它引发的异常。
要在 try-catch 块中包含带 throws 子句的方法的调用,必须提供该方法的调用者。

43.transient

用法:transient 关键字可以应用于类的成员变量,以便指出该成员变量不应 在包含它的类实例已序列化时被序列化。

-示例-

1
2
3
public class MyClass{
private transient String password;
}

java 语言的关键字,变量修饰符,如果用** transient 声明一个实例变量,当对象存储时,它的值不需要维持。
Java 的
serialization 提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用 serialization 机制来保存它。为了在一个特定对象的一个域上关闭 serialization,可以在这个域前加上关键字 transient**。
**transient 是 Java 语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient 型变量的值不包括在串行化的表示中,然而非 transient **型的变量是被包括进去的。

44.try

解释:监视,考验,审问
用法:**try 块用来监视所有又可能发生异常的代码,它可以和 catch 块组合,也可以和 final 块组合,也可以和 catch 块,final **块一起组合使用。
try 关键字用于包含可能引发异常的语句块。

-示例-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
try{
<可能引发异常的块>
}
catch (<java.lang.Exception 或子类> e){
<处理异常 e 的代码>
}
try{
<可能引发其他异常的块>
}
catch (FooException e){
<处理 FooException e 的代码>
}
catch (BarException e){
<处理 BarException e 的代码>
}
try{
<可能引发异常的块>
}
catch (<java.lang.Exception 或子类> e){
<处理异常 e 的代码>
}
finally{
<有异常或无异常情况下都执行的语句>
}

-注释-
每个 try 块都必须至少有一个 catch 或 **finally **子句。
如果某个特定异常类未被任何 catch 子句处理,该异常将沿着调用栈递归地传播到下一个封闭 try 块。如果任何封闭 **try **块都未捕获到异常,Java 解释器将退出,并显示错误消息和堆栈跟踪信息。

45.true

解释:真,布尔型的字面值
用法:boolean 类型的取值范围之一。
true 关键字表示
boolean
变量的两个合法值中的一个。

-示例-

1
boolean isComplete = true;

46.void

用法:void 关键字表示 null 类型。

-示例-

1
2
3
4
5
6
public class MyClass{
public void doSomething(){
<statements>
return;
}
}

-注释-
void 可以用作方法的返回类型,以指示该方法不返回值。

47.volatile

用法:volatile 关键字用于表示可以被多个线程异步修改的成员变量。
注意:volatile 关键字在许多 Java 虚拟机中都没有实现。

-示例-

1
2
3
public class MyClass{
volatile int sharedValue;
}

-注释-
**volatile 的目标用途是为了确保所有线程所看到的指定变量的值都是相同的。
Volatile 修饰的成员变量在每次被线程访问时,都强迫从主内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到主内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
Java 语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。
这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。
而
volatile 关键字就是提示 VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
使用建议:在两个或者更多的线程访问的成员变量上使用
volatile
。当要访问的变量已在
synchronized 代码块中,或者为常量时,不必使用。
由于使用
volatile **屏蔽掉了 VM 中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。

48.while

用法:while 关键字用于指定一个只要条件为真就会重复的循环。
-示例-

1
2
3
while (!found){
<statements>
}

Java 关键字总结
https://bubao.github.io/posts/cefe6d2e.html
作者
一念
发布于
2016年7月29日
许可协议