Java编程语言是强类型的,静态类型的语言; 每个变量或表达式都要在编译时有一个已知的类型。类型限制了变量所能代表的值,限制了表达式所能产生的值,限制了值所能支持的操作,也决定了操作的意义。强类型,静态类型有助于在编译时帮忙检测错误。
Compile time:将程序转化为独立于机器的二进制码。
Rum time:包括加载、链接程序执行需要的类,生成机器语言(可选),程序的动态优化以及执行程序。
原始类型和引用类型,以及特殊的"null" type
原始类型在所有的机器和实现上都是一样的;包括boolean类型,数字类型:整形(byte(8-bit)、short(16-bit)、int(32-bit)、long(64-bit))、浮点型(float, double)、和Unicode char(16-bit)类型。
byte、short、int、long是带符号整数。
char是不带符号的整数,代表UTF-16 字符。
引用类型包括: 类类型、接口类型和数组类型。引用类型指向类/数组的实例对象,一个对象可以有多个引用。
特殊的"null" type:没有名字,所以没有办法声明变量类型或者强制转换,不过"null reference"可以被转换成其他类型; 实际编程时,可以把null当成特殊的字面量。
很多情况下,装箱和拆箱是由编译器自动完成的。
Variables are typed storage locations:变量是类型化的存储位置。
枚举类,可以有自己的方法。
Java语言支持数组的数组而不是多维数组,这两者有什么区别吗?
有三种类型的exception:
Java语言不会自动初始化局部变量,以免掩盖编程错误。
字面量就是一个值,这个值可能是原始类型的,也可能是String类型或者null 类型的。
需要注意的一点是对于整型和浮点型字面量允许使用"_"来分割数字。
EscapeSequence(转义字符):
\ b (backspace BS, Unicode \u0008 )
\ t (horizontal tab HT, Unicode \u0009 )
\ n (linefeed LF, Unicode \u000a )
\ f (form feed FF, Unicode \u000c )
\ r (carriage return CR, Unicode \u000d )
\ " (double quote " , Unicode \u0022 )
\ ’ (single quote ’ , Unicode \u0027 )
\ \ (backslash \ , Unicode \u005c )
String 对象拥有常量值; String literal(String 字面量,如“java”)是String实例的引用,相同的String literal是同一个String实例的引用; 因为String literals 或者更广泛的来说,字符串类型的常量表达式是"interned"(关押), 所以使用String.intern方法可以共用同一个String实例。
字符串类型的常量表达式:
“The integer " + Long.MAX_VALUE + " is mighty big.”
这样的表达式会在编译时计算,所以会被当成String literal。
package testPackage;
class Test {
public static void main(String[] args) {
String hello = "Hello", lo = "lo";
System.out.print((hello == "Hello") + " ");
System.out.print((Other.hello == hello) + " ");
System.out.print((other.Other.hello == hello) + " ");
System.out.print((hello == ("Hel"+"lo")) + " ");
System.out.print((hello == ("Hel"+lo)) + " ");
System.out.println(hello == ("Hel"+lo).intern());同包同类内的String literal是同一个String实例的引用
}
}
class Other { static String hello = "Hello"; }
and the compilation unit:
package other;
public class Other { public static String hello = "Hello"; }
produces the output:
true true true true false true
This example illustrates six points:
• Literal strings within the same class (§8 (Classes)) in the same package (§7 (Packages
and Modules)) represent references to the same String object (§4.3.1).
同包同类内的String literal是同一个String实例的引用
• Literal strings within different classes in the same package represent references to the
same String object.
同包不同类内的String literal是同一个String实例的引用
• Literal strings within different classes in different packages likewise represent references
to the same String object.
不同包不同类内的String literal是同一个String实例的引用
• Strings computed by constant expressions (§15.28) are computed at compile time and
then treated as if they were literals.
字符串类型的常量表达式会在编译时计算,所以会被当成String literal
• Strings computed by concatenation at run time are newly created and therefore distinct.
字符连接的计算是在run time进行的,所以对应的String是新创建的,所以会不同。
• The result of explicitly interning a computed string is the same string as any pre-existing
literal string with the same contents.
对一个计算得来的string使用intern得到的引用会和已存在的string literal(相同内容)指向同一个String实例对象。
变量的类型:
类变量,实例变量和数组成员变量在被创建的时候会初始化为默认值。
类变量
在类的field域中被static修饰的变量,或者在接口声明的变量(有或者没有static都一样)。
实例变量
在类的field域中没有被static修饰的变量
数组成员变量(Array components)
数组成员是没有名字的变量,当数组创建的时候就别创建并初始化到了默认值。
方法参数变量
方法参数变量在方法调用的时候会被创建,并使用参数值初始化。
构造函数参数变量
当实例创建或者构造函数被调用的时候参数变量会被创建,并使用参数值初始化。
Lambda 参数变量
当Lambda表达式实现的方法被调用时,参数变量会被创建,并使用方法调用时的参数值初始化。
异常参数变量
当catch语句捕捉到异常时异常参数变量会被创建,新变量会使用和exception相关的对象初始化。
局部变量
当程序控制流进入一个语句块(block)或者for循环的时候,相应的局部变量便会被创建。
局部变量初始化可能使用了表达式,只有当表达式执行了,变量才会被初始化。
compile-time type & run-time type
run-time type:在Java 虚拟机里,每个对象都属于一些特殊的类,这类叫做"class of object“; 对象是这个类或者这个类所有父类的实例。数组对象也有对应的类。
变量的compile-time type是声明了的,表达式的compile-time type在编译时可以推断出来。
compile-time type和run-time type不一定是对应的,因为:
在run-time,classes和interfaces是JVM使用class loader加载的,所以同一个类使用不同的class loader加载后,在run-time中也是不一样的。
类型变量和类型参数在run-time是不可具体化的,所以run-time中的类或者接口可能代表了compile-time中的多个参数化的类型; 特别是泛型的compile-time参数化使用的是同一个run-time 类型。如:
Vector x = new Vector();
Vector y = new Vector();
x.getClass() 和 y.getClass()是相同的。
转换的类型:
class Test {
public static void main(String[] args) {
// 1.Casting conversion (5.4) of a float literal to
// type int. Without the cast operator, this would
// be a compile-time error, because this is a
// narrowing conversion (5.1.3):
int i = (int)12.5f;
// 2.String conversion (5.4) of i's int value:
System.out.println("(int)12.5f==" + i);
// 3.Assignment conversion (5.2) of i's value to type
// float. This is a widening conversion (5.1.2):
float f = i;
// 4.String conversion of f's float value:
System.out.println("after float widening: " + f);
// 5.Numeric promotion (5.6) of i's value to type
// float. This is a binary numeric promotion.
// After promotion, the operation is float*float:
System.out.print(f);
f = f * i;
// 6.Two string conversions of i and f:
System.out.println("*" + i + "==" + f);
// 7.Invocation conversion (5.3) of f's value
// to type double, needed because the method Math.sin
// accepts only a double argument:
double d = Math.sin(f);
// 8.Two string conversions of f and d:
System.out.println("Math.sin(" + f + ")==" + d);
}
}
Widening Primitive Conversion:
可能会丢失精度,但是不会发生run-time exception
• byte to short , int , long , float , or double
• short to int , long , float , or double
• char to int , long , float , or double
• int to long , float , or double
• long to float or double
• float to double
Narrowing Primitive Conversion:
可能会丢失数据,高位可能会丢失; 但是也不会发生run-time exception
• short to byte or char
• char to byte or short
• int to byte , short , or char
• long to byte , short , char , or int
• float to byte , short , char , int , or long
• double to byte , short , char , int , long , or float
byte to char的过程:byte会先转换成int,然后再转换成char。
装箱转换:
装箱转换可能会导致OutOfMemoryError, 因为包装类对象可能没有充足的空间。
• From type boolean to type Boolean
• From type byte to type Byte
• From type short to type Short
• From type char to type Character
• From type int to type Integer
• From type long to type Long
• From type float to type Float
• From type double to type Double
• From the null type to the null type
String conversion:
基本数据类型在转换为string 类型时,先转换为对应的包装类类型,再调用toString方法(如果是null,会直接会直接使用null 字符串)。对于值为x的基本数据类型T,如果 T 是 boolean类型, 那么使用new Boolean(x)。
6.1已经读完,主要讲了命名的规范。
shadowing:名字相同的变量,scope重叠的情况。
8.Classes
Top level class和nested class:
Nested class包括成员类,局部类和匿名类。
Inner class
non-static nested class是Inner class。
接口内的nested class默认是static的,所以不是一个inner class。
inner class不能有static初始化块,不能定义static成员(常量除外),但是可以继承static成员。
inner class实例中有一个外部类的实例(enclosing instance)
如果inner class访问了外部类的实例变量,那么在这个过程中外部类的实例对象会被用到。
静态内部类访问不了外部类的实例变量。
抽象类可以实现接口类,而不实现具体方法。
阻止类实例化的方法就是只声明一个private无参的构造方法,并且不调用它; 这种类一般是有类方法或者类变量。
父类的private成员是不会被子类继承的。
父类中只有声明为protected和public的成员才可以被非同package的子类继承。
构造函数,静态初始化块和实例初始化块都不属于成员,所以也就不会被子类继承。
一个非public的类,无法在其他package的直接访问,但是可以通过间接的方法访问(通过它的public的父类或者接口)。
子类中特定名字的field会hide父类中相同名字的field, 无论类型是否相同,无论static还是non-static。
transient 修饰的变量,会变化频繁, 不会被存放在永久性的内存区域。
volatile 修饰的变量用于多线程访问的情景,保证每个线程访问到的都是最新的值。
static常量会比其他static域先初始化。(初始化阶段就按照code赋值了,其他static只是赋默认值的意思?)
枚举类中可以声明abstract方法。
在run-time,machine-code生成器或者优化器可以将final方法内联(inline)。
native方法是用平台相关的code实现的,比如C。
synchronized方法在执行之前需要一个monitor,对于class 方法,这个monitor是这个class相关的Class 对象; 对于实例方法,这个monitor是对象实例(this)。
子类不能从superinterface中继承private和static方法。
static方法不能被overriden,但是可以被hiden。
static方法不能hiden实例方法,但static field可以hiden实例field。
可以通过super. 或者强制类型转换来访问父类中被hiden的方法,但是不能通过将子类强制类型转换为父类类型来访问父类中被override的方法。
如果调用的是实例方法,那么在run-time才会确定真正调用的方法(dynamic method lookup)。
一个非私有的(non-private)内部类,可以被编译它的编译器访问,也可以被其他编译器访问,所以二进制名字是有规范的(predictable); 但是局部内部类或者匿名内部类只能被编译它的编译器访问。