Java - jwyx/ForFun GitHub Wiki

Credit

caterpillar.onlyfun.net/Gossip/

Eclipse

快捷键设置: window-preferences-general-keys
模板补全:window-preference-general-keys(Content Assist(Template Proposals))
Unix兼容性设置:http://hi.baidu.com/lane727/item/72339a5ee4958b19db163521
快捷键查看:Ctrl+Shirt+L
窗口切换:Ctrl+F7
代码补全:window-preference-general-keys(Context Information)

Java 基础

class定义类 
  E.g. 
    public class HelloWorld { 
        public static void main(String[] args) {
            System.out.println("Hello World!");
        }
    }
    类名:HelloWorld
    一个文件可以包含多个类,但是只能有一个public,而且文件名必须与public类名相同。
    main()方法是Java程序的进入点;程序执行由进入点开始;需要被调用,并且在产生对象之前要执行,所以需要public和static。
    void表示没有返回;String[] args是command line argument。

System.in提供的read()是从输入串流取得一个byte的数据,并传回该byte的整数值。
java.util.Scanner使用空白符来间隔每一个输入字串。
java.io.BufferedReader:使用readLine()方法必须处理IOException;BufferedReader在创建时接受一个Reader对象,
    InputStreamReader继承至Reader
readLine()方法会返回用户在按下Enter之前输入的所有字符。

printf(), print(), println()
err, in, out:标准错误,输入,输出
%n是输出平台特定的换行符,如果在windows下换成'/r/n',在linux下换成'/n'
%%显示百分号;
输出格式由java.util.Formatter来提供

java.io.Console是主控台,以javaw所执行的程序,比如Eclipse和NetBeans等IDE时,就没有主控台,使用System.console()只会传回null

Built-in data types:
    Integer: short(2 bytes), int(4 bytes), long(8 bytes)
    Byte
    Float: float(4 bytes), double(8 bytes)
    Character: 采用Unicode编码,前128个字符编码同ASCII编码,2 byte,存储范围从\u0000到\uFFFF
    Boolean: true/false,Java将Boolean抽象化,底层没有实际对应的类型,也不允许其他类型与Boolean类型之间的转换

Java中使用没有初始化的变量会报错
0.0默认是double类型
Literal constant:无法修改
Constant variable:在声明时添加final,这个变量一旦指定值就不能更改。

如果类型转换是从精度大到精度小,必须显式指定,不然报错

==对于对象来说,表示两个对象是否是同一个对象,而不是比较其中内容

Logical operator
Bitwise operator

<<:左移指定的位数,右边补0
>>:右移指定的位数,左边补0或1,视最左边原来的bit
>>>:右移,左边补0

Increment operator
Decrement operator
++/--直接在变量的存储空间运算,而不用取出变量值,运算再将数值存回变量的内存空间;所以效率更高
Assignment operator

switch只能比较Integer和Character

break本来只能离开for,设定标识与scope,则可以离开整个scope;
返回back处,之后整个back区块不执行而跳过
E.g.
    back : {
        ...
        break back;
        ...
    }

continue配合标识,可以自由的跳至任何一层for循环

在Java中,处理的东西几乎都是对象;但是primitive data type不是对象,比如int,double,boolean等变量,以及字面常量都不是对象
使用包裹类型(Wrapper Types)才能将基本类型转换为对象。
E.g. Integer integer = 10; // Auto-boxing
同样的方式适用于boolean, byte, short, char, long, float, double对应于Boolean, Byte, Short, Character, Integer, Long, Float, Double
更一般化的类型 Number
E.g. Integer fooInteger = 10; int fooInt = fooInteger; // Unboxing
等价于 Integer fooInteger = Integer.valueOf(10);

Auto-boxing和Unboxing是在编译器编译期实现,是Compiler sugar。
小心使用:当值为-128~127之间,装箱为Integer后,会存在memory中被重用;当值超过了-128~127范围,每次都重建一个Integer。

Array
    int[] arr = new int[10];
    int[] score = {90, 10};
    如果Array没有赋初值,预设值为0或false
    从0开始

    int[][] arr = {{1, 2, 3}, {4, 5, 6}};
    int[][] arr = new int[2][3];
    不等长数组
        int[][] arr = new int[2][];
        arr[0] = new int[3];
        arr[1] = new int[2];
    arr是一个一维数组的参考变量,所以它可以参考一个任意长度的一维数组。
    值复制的实现:System.arraycopy(来源,起始索引,目的,起始索引,复制长度)
    
    java.util.Arrays提供一些静态方法,sort(), binarySearch(), fill(), equals()。
    deepEquals(), deepToString():对二维乃至三维以上的数组进行操作
    copyOf():直接返回一个新的数组对象,而且复制原数组内容。
    数组中存放对象时,存放对象位置的索引;存储基本数据类型时,存储值。
    foreach的语法:for(type element : array) { ... }

String
    字符串使用Unicode字符来存储
    String text = "字符串";
    + 串接
    length(), equals(), toLowerCase(), toUpperCase()
    Immutable:字串对象一旦被配置,他的内容就是固定不变的
    在Java中,使用=将一个字串指定给变量,该变量指向新的字符串。
    在Java中,会维护一个String Pool,对于可以共享的字串对象,会先在String Pool中查找是否存在相同的String内容,有直接返回,否则创建新的。
    intern()使用Flyweight模式
    split()
    java.util.regex.Pattern
    matches():是否符合指定的正则表达式;调用了Pattern的静态方法matches(),返回boolean值
    replaceAll():将符合正则表达式的子字串置换为指定的字串
    Pattern的静态方法compile()对正则表达式进行编译,返回Pattern对象,使用其matcher()方法进行字串比较,会传回Matcher对象
    StringBuilder产生的对象默认会有16个字符长度,也可以指定初始长度,如果字符超过可容纳的长度,则StringBuilder对象会自动增加长度
    StringBuilder:length()会返回目前对象中的字符长度;capacity()返回该对象目前可容纳的字符容量。
    StringBuilder和StringBuffer具有相同的接口,但是StringBuffer是线程安全的。

    args索引0的值是从程序名称后第一个参数开始。

    this()方法用于对象内部,表示调用对象的构建方法;或者this表示对象本身

    对象在构建之前,对象的属性必须先初始化完毕才能执行构建函数。

    public class Test {
        {
            System.out.println("initial");
        }

        public Test() {
            System.out.println("Test");
        }
    }
    在{与}之间的程序,会在运行期自动加入到对象构建流程的开头(而不是编译时期),也就是先执行{与}间的程序,再执行指定的构建方法

相同类的对象拥有各自的对象数据成员,但是共享一份代码。调用方法函数时,会隐式传入一个this,this指向调用该方法的对象。

static表示类数据成员,被对象共享;使用 类名.类数据成员名 来调用
添加static的方法成员,称为静态方法,静态方法一般为了提供工具; 使用 类名.静态方法 来调用
调用静态方法时不会传入this,其中无法调用非静态函数

public class Ball {
    public static int[] arr = new int[10];
    static {
        // initialize
    }
}
在第一次调用类时,类才被载入,static区块的代码会被执行,且只会执行一次,要注意的是,static属性成员必须在static区块之前;
而如果在static区块中发生了意外,则会包装为java.lang.ExceptionInitializerError

Overload 重载
编译器处理重载时对函数的选择顺序:
    1. 是否有符合原类型参数的方法
    2. 是否有符合Auto-boxing之后新类型参数的方法
    3. 是否有Auto-boxing和不定长引数的方法
    4. 找不到合适方法,回报编译错误

不定长引数 Variable-Length Argument
    在声明参数时,在类型关键字之后加上...;E.g. int... nums;
    在方法上设定不定长参数时,必须设定在参数列表的最后一个,也没办法设定多于一个不定长参数;
    E.g. printf()

Recursion 递归
Garbage collection 垃圾回收
    在适当的时候,Java运行环境会自动检查对象,看看是否有未被引用的对象,如果有的话,会清除对象,回收对象占据的内存空间
    垃圾回收的时机我们并无法得知,可能是内存资源不足,或是程序空闲的时候。
    在Java中并不明确有析构方法,但是Java有finalize()方法,属于protected,会在对象被回收时执行;
    可以使用finalize()来进行一些相关资源的清除动作,而这些动作与立即性的收尾动作没有关系。
    当确定某个对象不再使用,将null赋值给变量,使用System.gc()建议程序进行垃圾回收,如果被回收,回收前执行finalize()。

extends 继承
super() 调用父类中的函数

如果在定义成员时没有指定任何的存取修饰,则为默认default的存取权限,即可以在同一个package中的其他类直接存取。

Override 重新定义
    super或者super()的使用
        如果在子类中想要呼叫父类的构建方法,使用super()
        如果在子类中呼叫父类的方法,使用super.methodName()
        但是使用super()或super调用父类中方法的条件就是 父类中该方法不能是private
    但是不可以缩小父类的方法权限
    在重新定义方法时,可以重新定义返回值,但是有限制,必须是父类中同一方法返回值类别的子类,或是实现接口的类
    注意,只能重新定义非static的方法,如果你在子类中实现一个有同样signature的static成员,并不是重新定义,而是定义了一个属于子类的static成员

Object
    所有类都继承于Object类
    Object定义了protected: clone(), finalize();public:equals(), toString(), getClass(), hashCode(), notify(), notifyAll(), etc.
    但是被宣告为final的方法无法重新定义

final
    修饰变量:表示该变量一旦设定,就不可以再改变变量的值
    修饰方法成员:表示该方法无法被重新定义(Override)
    private方法,无法被继承也无法被重新定义,自然是final成员,无需在private的方法成员上另外加上final
    修饰类:表示不能被继承;E.g. public final class ClassName { ... }
    修饰数据成员,但是未初始化,则数据成员必须在构建方法中进行初始化,且初始化后不能改变其值
    final放在类型名之前最靠近类型名的位置

Polymorphism 多态
    Abstract method:只声明方法,但不做定义
    Abstract class:包含抽象方法的类,不能生成实例;适用于is-a的场景
    在Java中,声明抽象方法和抽象类,使用abstract关键字
    E.g. public abstract class ClassName { ... }

Interface 接口
    定义一组可操作的方法;所有方法都只有声明没有实现
    interface 接口名称 {
        返回值 方法名(参数列表);
        ...
    }
    接口中方法默认情况下的访问权限是public;接口本身默认是abstract,所以不需要显式添加
    当实现接口时,可以使用implements关键字指定要实现的接口名,接口中所有定义的方法都需要实现
    因为接口中的方法默认是public,所以实现类中的对应方法都必须设置为public,否则无法通过编译
    Java中只能单一继承,使用接口实现多重继承
    public class 类名 implements 接口1, 接口2, 接口3 { ... }
    
    接口本身也可以继承自其它接口
    public interface 名称 extends 接口1, 接口2 { ... }
    不同于类,接口可以继承自多个接口
    接口转换 E.g. ISomeInterface1 obj1 = (ISomeInterface1) someObject;

资源管理

Inner class:类中定义类
    非static的内部类分为:member inner class, local inner class, anonymous inner class
    使用内部类的好处就是直接存取外部类的私有成员;当某个类只服务于另一个类时,可定义为内部类
    内部类同样可以使用public, private, protected
    local inner class定义于一个方法中;类的可视范围和生成对象仅在该方法中。
    anonymous inner class可以不声明类名称,而是使用new直接产生一个对象,可以是继承某个类或接口
        E.g. new [类或接口] { ... }   
        如果要在内部匿名类中使用某方法中的变量,该变量必须声明为final
        因为对于local变量,并不是真正被用于匿名类对象中,而是拷贝一份,所以做任何修改,不影响原来的变量
    添加static的内部类,只能在外部类的static方法中使用
    也可以看成另外一种名称空间的管理方法

    在文件管理方面,内部类在编译完成后生成文件名为[外部类名$内部类名.class];
    而内部匿名类则在编译完成后生成[外部类名$编号.class],编号为1,2,3,看他是外部类的第几个匿名类

Package
    管理名称空间
    被设计为与文件系统结构相对应,编译时添加-d自动建立对应结构的文件目录
    使用语法:package xx.xxx;
    package名称成为类名称的一部分,除非重新编译否则无法改变名称
    使用方式:
        1. 完全吻合(Fully qualified);E.g. xx.xxx.Point2D
        2. 使用import关键字;告诉编译器使用的类位于拿一个package下; 
           E.g. import xx.xxx.Point2D; import xx.xxx.*;
           需要注意名称冲突
    如何跨package使用
        如果类存在于不同的package下,类必须声明为public,否则只能在同一个package中使用
        如果类的定义没有指定public,private,protected,那么默认为Default,成员只能在同一package中被直接存取,且无法在子类中被直接存取
        取package的class也和CLASSPATH的设定有关

Constructor
    没有定义则编译器自动生成一个无参数的构建函数
    如果有自定义的构建函数,则编译器不生成构建函数
    如果在继承时没有使用super()指定要使用的父类构建函数,则默认使用无参数的构建函数
    构建函数的存取权限同类的存取权限相同

存取权限
              | 同一个类 | 同一个package | 子类 | 全局
    private   | OK      |              |      |
    default   | OK      |     OK       |      |
    protected | OK      |     OK       |  OK  |
    public    | OK      |     OK       |  OK  | OK

常量定义
    接口和类中
    E.g. public static final int TURN = 1;
    或枚举类,这是一个类,不是基本类型
    public enum OpConstants { TURN_LEFT, TURN_RIGHT }
    适合定义成inner class

    用在switch中,编译器会检查是否属于枚举值

import static 静态成员
    E.g. import static java.lang.System.out;
    如果引入类下面的所有静态成员,可以使用*
    当编译器遇到命名冲突时的解决方式
        1. 区域变量覆盖
            选用方法中的同名变量,参数,方法
        2. 成员覆盖
            选用类中定义的同名数据成员,方法成员
        3. 重载方法比对
            使用import static的各个静态成员,若有命名冲突,尝试通过重载判断

Exception handling 和 Assertion
    Java的Exception handling可以协助我们捕捉程序运行时期的错误,用来处理一些系统可以恢复的错误
    Exception是Java中定义的一种类,在特定错误发生时会丢出Exception对象,我们可以捕获并且加以处理
    E.g.
        try {
            // 陈述句
        }
        catch (例外类型 名称) {
            // 例外处理
        }
        finally {
            // 一定会处理的区块
        }
    一个try,必须有对应的catch,可以有多个catch,而finally可有可无,如果没有catch,则一定要有finally
    finally无论是否出错都会执行
    好处就是 将逻辑和错误处理分开

    throw new ArithmeticException();
    try-catch可以嵌套
    方法可能会因为错误引发例外,但是不希望在这些方法中处理例外,希望在调用的方法中处理,那么使用throws关键字来声明这个方法将丢出例外
    E.g. private void method() throws java.io.IOException { ... }
    有多个例外会丢出时,在throws中使用逗号分隔Exception
    当方法上使用throws声明例外,调用者必须对其作出处理或者继续throws
    如果继承
        父类中某些方法声明了throws,那么子类中重新定义该方法时可以
            1. 不处理例外(重新定义时不设定throws)
            2. 可仅throws父类中被重新定义的方法上某些例外
            3. 可以throws被重新定义方法上例外的子类
        但是不可以
            1. throws出额外的例外
            2. throws被重新定义的方法上例外的父类
    编译时期错误:编译器检查语法相关的错误
    运行时期错误:在运行时期发生的逻辑错误,或者因为IO,网络,内存不足引起的错误
    
    例外的分类
        java.lang.Error 严重错误:硬件错误,内存不足
        java.lang.Exception 非严重错误:可以处理的错误
        Error和Exception都继承自Throwable,其中包括getLocalizedMessage(), getMessage(), printStackTrace()等
        继承架构
            Throwable
                Error
                    LinkageError
                    ThreadDeath
                    VirtualMachineError
                    ...
                Exception
                    ClassNotFoundException
                    CloneNotSupportedException
                    IllegalAccessException
                    ...
                    RuntimeException
                        ArithmeticException
                        ArrayStoreException
                        ...
        子类例外catch处理必须放在父类处理之前
        如果自定义例外,继承至Exception而不是Error

        assertion语法
            assert <boolean_expression>;
            assert <boolean_expression> : <detail_expression>;

            默认执行时不启动assert;使用-enableassertions/-ea和-disableassertions/-da来控制
            通常在开发阶段使用,检查假设是否成立

Enumerated Type
    定义枚举类型,本质上就是定义一个类
    继承自java.lang.Enum,每一个成员其实就是该类型的一个实例,他们被预设为final, static, public
    toString():获得枚举值的字符串描述;println()会自动调用toString()
    values():可以获得所有的枚举实例
    equals():默认根据枚举的字符串值比较
    valueOf():将指定的字符串转化为枚举实例
    compareTo():比较两个枚举对象在枚举时的顺序;1,0,-1表示在被比较的枚举对象(参数)之前,相同,之后
    ordinal():根据枚举顺序得到位置索引,默认为0开始

    枚举定义时可以加入方法
    public enum OpConstants {
        TURN_LEFT, TURN_RIGHT;
        public String getDescription() { ... }
    }

    枚举类型,可以添加构建方法,但是不能为public
    在定义枚举类型时,可以实现接口

    每个对象实现自己的方法 (Value-specific Class Bodies)
    public enum OpConstants {
        TURN_LEFT {
            public String getDescription() { ... }
        },
        TURN_RIGHT {
            public String getDescription() { ... }
        };
    }

Generics

类定义逻辑完全一致,但是成员类型不同 => 泛型(Generics)
<T>来声明一个type holder
public class GenericFoo<T> {
    private T foo;
    public void setFoo(T foo) { this.foo = foo; }
    public T getFoo() { return foo; }
}
GenericFoo<Boolean>和GenericFoo<Integer>属于不同的类
可以声明多个Type holder
holder可以用于数组
Generics定义时可以使用其他已经定义的Generics

<T extends List>允许特定的类型,可以使用接口或者类
<T>本质上是<T extends Object>
<? extends List>允许指向特定的类型
<?>本质上是<? extends Object>
使用ClassName<?>和ClassName类型变量指向对象的区别就是用ClassName<?>指向的对象可以获取属性或者删除属性,但不能添加新的属性值
GenericFoo<? super String> foo; // foo只能接受String或其父类的对象
GenericFoo<? extends String> foo; // foo只能是String或其子类的对象
可以继承父类或实现接口
public class SubGenericFoo<T1, T2, T3> extends GenericFoo<T1, T2> { ... }
// 如果父类上的type holder不齐全,那么继承下来的holder自动变成为Object
public class GenericFoo<T1, T2> implements IFoo<T1, T2> { ... }    

Java Essence

source code: *.java
byte code:   *.class; compiled
JVM (Java Virtual Machine): 将byte code翻译成相应平台的机器语言
对于Java程序,只认识JVM,byte code就是它的可执行文件。

JRE (Java Runtime Environment)
Java不仅仅是一个语言,更是一个标准。所谓的标准函数库,也叫Java SE API。
Java程序会引用标准函数库,并执行在JVM这个唯一认识的系统之上,这样才能达到跨平台。标准函数库及JVM都是包含在JRE中。
标准函数库由JRE提供,而所有的程序都是执行在JVM上。

Java SE平台,参见http://java.sun.com/javase/technologies/index.jsp

JDK (Java Development Kit) include Development Tools, Private JRE, Source Code, or Public JRE

PATH:添加C:\Program Files\Java\jdk1.7.0_13\bin到PATH中
CLASSPATH:按照CLASSPATH中的路径寻找.class文件;缺省则读取当前目录下的.class
实际上,CLASSPATH的路径设定,是给应用程序类载入器(AppClassLoader)使用的参数。

OS依PATH的路径来寻找可执行指令;JVM依据CLASSPATH的路径来寻找.class文件

java C:\workspace\HelloWorld -> 启动JVM,之后接下类名,表示由JVM载入该类的.class并执行
java -classpath c:\workspace;c:\classes HelloWorld

JAR Archive:.jar是使用zip格式压缩,其中包含一对.class文件;当作一个特殊的文件夹,在CLASSPATH中指定
    
java执行时,寻找JRE顺序是:
    1. 是否可在java的bin目录下找到相关原生程序库(.dll,例如java.dll)
    2. 是否在上一层目录中找到jre目录
    3. 查看启动项HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment\的版本与目录信息
PS:如何打开注册表 'regedit'

java -version 显示所执行的JRE版本

如果需要切换JRE,则设定PATH指向所想要的JRE的bin目录,而不是设定CLASSPATH。

在JDK的bin目录下,所看到的.exe只是Wrapper;真正的编译器等工具程序,其实是位于JDK目录下lib目录中的tools.jar。

真正java编译器的程序进入点,其实是在com.sun.tools.javac.Main这个类,执行javac其实执行的指令就是:
    java -cp "C:\Program Files\Java\jdk1.7.0_13\lib\tools.jar" com.sun.tools.javac.Main

JDK中多数工具程序,本身就是用Java写的。

编译器对每个.class文件,都会表示主版本号与次版本号,不同的版本号意味着是由不同的JDK编译出来,而.class的格式可能有所不同。
可以使用javap来确定.class的版本号。 E.g. javap -verbose HelloWorld

javac可以使用-target指定编译出来的.class必须符合所允许的版本号。
-source指定检查是否使用到指定版本意外的语法,没有的话再编译。所以-target必须大于-source。
    
javac -verbose 显示编译细节。
默认情况下,搜索.java的路径和搜索.class的目录相同。但是还能另外指定-sourcepath来指定搜索.java的路径。
默认情况下,生成的.class与.java目录相同。但是可以指定-d来指定.class的输出目录。

文件管理

用关键字package做类的分类管理。 如在.java文件开头 package cc.openhome; 则所有之后的类变成cc.openhome.类名
Fully Qualified Name

在编译时加上-d,这可以让你指定编译出来的.class文件要放置的文件夹;
如果类中包含了package分类,则在你指定的文件夹下,就会建立起对应package阶层的文件夹,并将.class放进去。
E.g. javac -d c:\workspace Main.java
⚠️ **GitHub.com Fallback** ⚠️