第1章 对象导论
1 Java用三个关键字在类的内部设定边界:public,protected,private。
2 如果组合是动态发生的,那么它通常被称为聚合。组合经常被视为“has-a”关系。继承经常被视为"is-a"关系
3 继承用关键字extends。子类改变基类的方法称为"覆盖"(overriding)
4 Java采用动态绑定。把将导出类看做是它的基类的过程称为向上转型。
5 Java中所有的类都继承自单一的基类,即Object
6 Java采用动态内存分配方式,即创建新对象时要使用new关键字来创建实例
7 Java提供了“垃圾回收”机制
第2章 一切都是对象
1 String s ; 该语句只是创建一个引用,s并没有跟任何事物相关联。如果相关联一个对象,通常用new操作符实现,如下
String s = new String("abc") ;
2 程序运行时,对象会存储在如下的五个地方
- 寄存器:最快的存储器,因为它在处理器内部。但寄存器数量有限
- 堆栈:位于通用RAM(随机访问存储器),通过堆栈指针从处理器获得直接支持。速度仅次于寄存器。对象引用一般存在这里,但是java对象并不存在这里
- 堆:通用的内存池(也位于RAM区),用于存放所有的java对象。
- 常量存储:常量值通常直接存放在程序代码内部。
- 非RAM存储:如果数据完全存活于程序之外,那么它可以不受程序的任何控制,在程序运行时也可以存在。
3 Java不是用new来创建变量,而是创建一个并非是引用的"自动"变量,这个变量直接存储"值"并置于堆中。
4 Java要确定每种基础类型所占存储空间的大小,并不会随机器硬件架构的变化而变化,如下
5 boolean类型所占存储空间的大小没有明确指定,仅定义为能够取字面值true或false
6 java提供了两个用于高精度计算的类:BigInteger和Decimal
7 java会确保数组会被初始化,并且不能在它的访问之外被访问。
8 尽管以下代码在C和C++是合法的,但java不能这么写,如下所示,x会被java判断为定义了2次而报错
{
int x = 12;
{
int x = 96; // x被重定义
}
}
9 java对象不具备和基础类型一样的生命周期,当new创建一个对象时,它可以存活于作用域之外
{
String s = new String("a string");
}
引用s会在作用域外就消失了,但s指向的String对象仍继续占据内存空间。垃圾回收器会监视用new创建的所有对象,并辨别那些不会再被引用的对象
10 java用class定义一个新类,可以用new创建这个类的对象。要使用该对象的成员变量或成员函数,在对象引用的名称后面紧跟一个句点,再接着成员名称或成员函数,即objectReference.member,如下
class DataOnly {
int i;
double d;
boolean b;
}
DataOnly data = new DataOnly();
data.i = 1;
data.d = 1.2;
data.b = false;
11 类的成员变量是基础数据类型时,即使没有初始化,java也会确保成员变量有个默认值,默认值如下
12 局部变量不会有初始化,所以在使用前一定要赋值,不然java会在编译时就报错
13 java的基本组成部分包括:返回类型,函数名称,参数和方法体,如下
ReturnType methodName(/*Argument lsit */) {
/* Method body */
}
14 Java设计者希望程序员反过来使用自己的Internet域名声明库名。import指示编译器导入一个包,即一个类库。如
import java.util.ArrayList ;
15 当用new创建对象时,数据存储空间才会被分配,其方法才供外界调用。
16 当声明一个事物是static时,就意味着这个域或方法不会与包含它的那个类的任何对象实例有关联。无须创建该类的任何对象,也可以调用其static方法或访问其static变量。即类成员和类方法。如下
class StaticTest{
static int i = 47;
static void fun(){}
}
int a = StaticTest.i; // 无须创建类对象,即可调用
StaticTest.fun() // 无须创建类对象,即可调用
该类的所有对象实例都共享同一个类成员和类方法。
17 类的名字必须和文件名相同。如果创建一个独立运行的程序,那么文件中必须存在某个类和文件同名,且该类必须包含一个名为main()的函数
18 java有两种注释方法,/**/ 和 //
19 java的代码风格:类名的首字母要大写,如果类名是由几个单词构成,就并在一起,每个单词的首字母都大写,不用下划线分隔,如下 AllTheColorsOfTheRainbow,即驼峰风格。方法,字段及对象引用名称都采用首字面小写。
第3章 操作符
1 String类支持操作符"+"和"+="
2 String后面紧跟一个"+",而"+"后面跟着一个非String类型的元素,编译器会把这个非String类型的元素转换为String类型。
3 基础类型存储了实际的数值,并非指向一个对象的引用
4 在为对象赋值的时候,操作的是对对象的引用。这种现象被称作“别名现象”
class Tank {
int level;
}
public class HelloDate {
public static void main(String[] args){
Tank t1 = new Tank();
Tank t2 = new Tank();
t1.level = 42;
t2.level = 27;
t1 = t2; // 是将t2对象的引用赋值给t1
t1.level = 34; // t1的修改其实就是对t2的修改
System.out.println("t1 = "+t1.level+", t2 = "+t2.level);
}
}
5 函数调用中的别名问题,即修改形参会影响到实参,如下
class Letter {
char c;
}
public class HelloDate {
static void f(Letter y){
y.c = 'z';
}
public static void main(String[] args){
Letter x = new Letter();
x.c = 'a';
System.out.println("1: x.c="+x.c); // 1: x.c=a
f(x); // 传递的是对象x的引用,所以函数修改的也会影响到对象x
System.out.println("2: x.c="+x.c); // 2: x.c=z
}
}
6 java支持前缀式自增,后缀式自增,前缀式自减,后缀式自减,如下
int i = 1;
System.out.println("i : "+i); // i : 1
System.out.println("++i : "+ ++i); // ++i : 2
System.out.println("i++ : "+i++); // i++ : 2
System.out.println("i : "+i); // i : 3
System.out.println("--i : "+ --i); // --i : 2
System.out.println("i-- : "+i--); // i-- : 2
System.out.println("i : "+i); // i : 1
7 用关系操作符 == 和 != 比较对象时,比较的式是对象的引用。对象如果要比较内容可以用成员函数equals(),但是equals()默认是比较引用,在自定义的类里需要覆盖equals()
Integer n1 = new Integer(45);
Integer n2 = new Integer(45);
System.out.println(n1 == n2); //false
System.out.println(n1.equals(n2)); //true
// equals()默认是比较引用,在自定义的类里需要覆盖equals()
class Letter {
char c;
public Letter(char c){
this.c = c;
}
}
Letter t1 = new Letter('a');
Letter t2 = new Letter('a');
System.out.println(t1 == t2); //false
System.out.println(t1.equals(t2)); //false
8 当使用逻辑操作符时,会有一种“短路”形象,即一旦能够明确无误地确定整个表达式值就不再计算逻辑表达式余下部分。如下所示,因为test2(2)为false,则整条逻辑表达式已明确为false,test3()无须执行,输出结果为
test1(0)
result:true
test2(2)
result:false
expression is false
static boolean test1(int val){
System.out.println("test1("+val+")");
System.out.println("result:"+(val<1));
return val < 1;
}
static boolean test2(int val){
System.out.println("test2("+val+")");
System.out.println("result:"+(val<2));
return val < 2;
}
static boolean test3(int val){
System.out.println("test3("+val+")");
System.out.println("result:"+(val<3));
return val < 3;
}
public static void main(String[] args){
boolean b = test1(0) && test2(2) && test3(2);
System.out.println("expression is "+b);
}
9 直接常量
int i1 = 0x2f; // 十六进制
System.out.println("i1: "+Integer.toBinaryString(i1));
int i2 = 0X2F; // 十六进制
System.out.println("i2: "+Integer.toBinaryString(i2));
int i3 = 0177; // 八进制
System.out.println("i3: "+Integer.toBinaryString(i3));
char c = 0xffff;
System.out.println("c: "+Integer.toBinaryString(c));
byte b = 0x7f;
System.out.println("b: "+Integer.toBinaryString(b));
short s = 0x7fff;
System.out.println("s: "+Integer.toBinaryString(s));
long n1 = 200L;
long n2 = 200l;
long n3 = 200;
float f1 = 1;
float f2 = 1F;
float f3 = 1f;
double d1 = 1d;
double d2 = 1D;
10 指数记数法。编译器通常会把指数作为双精度数处理
float expFloat = 1.39e-43f;
expFloat = 1.39E-43f;
System.out.println(expFloat); //1.39E-43
double expDouble = 47e47d;
double expDouble2 = 47e37;
System.out.println(expDouble); //4.7E48
11 按位操作符用来操作基础数据类型的单个比特,即二进制。与(&),或(|),异或(^),非(~)。并且可以和等号联合使用(&= , |= , ^= , ~= )
12 移位操作符操作对象的二进制,左移位操作符(<<)能按照操作符右侧指定的位数将操作符左边的操作数向左移动(在低位补0),右移位操作符(>>)能按照操作符右侧指定的位数将操作符左边的操作数向右移动。“有符号”右移位操作符使用"符号扩展":若符号为正则在高位插入0,若符号为负则在高位插入1。”无符号“右移位操作符(>>>)使用零扩展,即无论正负都在高位插入0.
int i = -12;
System.out.println(Integer.toBinaryString(i)); // 11111111111111111111111111110100
System.out.println(Integer.toBinaryString(i>>3)); // 11111111111111111111111111111110
System.out.println(Integer.toBinaryString(i>>>3)); // 11111111111111111111111111110
int j = 100;
System.out.println(Integer.toBinaryString(j)); // 1100100
System.out.println(Integer.toBinaryString(j>>3)); // 1100
System.out.println(Integer.toBinaryString(j>>>3)); // 1100
int k = 5;
System.out.println(Integer.toBinaryString(k)); // 101
System.out.println(Integer.toBinaryString(k << 2));// 10100
13 三元操作符 if-else,也被称为条件操作符。 boolean-exp ? value0 : value1 当boolean-exp为true时计算value0,若为false则计算value1
14 类型转换的一种方法:将希望得到的数据类型置于圆括号中,放在要进行类型转换的值的左边,即(type) value,如下
int t = 0;
long n = (long)t; // 将t从int类型转换为long类型
15 java允许我们把任何基本数据类型转换成别的基本数据类型,但布尔型除外,布尔型不允许进行任何类型的转换处理。“类”数据类型不允许进行类型转换,如果要转换要采用特殊的方法。
16 将浮点数转换为整型值,数据会被截掉,而不是四舍五入。如下所示,输出都是0
double above = 0.7,below = 0.4;
float fabove = 0.7f,fbelow = 0.4f;
System.out.println("(int)above:"+((int)above));
System.out.println("(int)below:"+((int)below));
System.out.println("(int)fabove:"+((int)fabove));
System.out.println("(int)fbelow:"+((int)fbelow));
如果想要得到四舍五入结果可以使用java.lang.Math的round()方法
17 表达式只有基础数据时,表达式中出现的最大的数据类型决定了表达式最终结果的数据类型,如float值和double值相乘,结果就是double值。int值和long值相加则结果是long值
第4章 控制执行流程
1 java支持if-else,while,do-while,for,return,break以及switch。java并不支持goto语句,但仍然可以进行类似goto那样的跳转。
2 if-else 语句
static int test(int v1,int v2){
if(v1 > v2){
return 1;
}else if(v1 == v2){
return 0;
}else{
return -1;
}
}
3 while,do-while和for用来控制循环,有时会被划分为迭代语句。while会比do-while少执行一次
// while
while(boolean-expression)
statement
// do-while
do
statement
while(boolean-expression);
// for
for(initialization;boolean-expression;step)
statement
4 for语句中可以定义多个变量,用逗号隔开,但它们必须具有相同的类型
for(int i=1,j=i+10;i<5;i++,j=i+2){
System.out.println("i = "+i+",j = "+j);
}
5 Java SE5引入了foreach语法用于数组和容器,不必创建int变量去对由访问项构成的序列进行计数,foreach自动产生每一项。如 for( float item : array )
Random r = new Random(47);
float f[] = new float[10];
for(int i=0;i<10;i++)
f[i] = r.nextFloat();
for(float x:f){ // foreach语法
System.out.println(x);
}
任何返回一个数组的方法都可以使用foreach。如String类有一个方法toCharArray(),它返回一个char数组。
for(char c:"An African Swallow".toCharArray()){
System.out.print(c+" ");
}
6 break用于强行退出循环,不执行循环中剩余的语句。而continue则停止执行当前的迭代,然后直接开始下一次迭代。
7 goto仍是Java中的一个保留字,但在语言中并未使用它;Java不支持goto。但采用标签机制通过break和continue来实现类似跳转的操作。
public static void main(String[] args){
int i = 0;
outer: // 标签
for(;true;){
innert: // 标签
for(;i<10;i++){
System.out.println("i = "+i);
if(i == 2){
System.out.println("continue");
continue;
}
if(i == 3){
System.out.println("break");
i++;
break;
}
if(i == 7){
System.out.println("continue outer");
i++;
continue outer; // 跳转至outer继续进行
}
if( i == 8){
System.out.println("break outer");
break outer; // 跳出outer
}
for(int k=0;k<5;k++){
if(k == 3){
System.out.println("continue inner");
continue innert;
}
}
}
}
}
在Java里需要使用标签的唯一理由是因为有循环嵌套存在,而且想从多层嵌套中break或continue。
8 switch里的选择因子必须是int或char那样的整数值,或enum,不能把浮点数或字符串作为选择因子使用
switch(integeral-selector){
case integral-value1: statement;break;
case integral-value2: statement;break;
case integral-value3: statement;break;
case integral-value4: statement;break;
// ...
default: statement;break;
}
public static void main(String[] args){
Random r = new Random(47);
for(int i=0;i<100;i++){
int c = r.nextInt(26)+ 'a';
System.out.print(((char)c)+". "+c+": ");
switch(c){
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':System.out.println("vowel");break;
case 'y':
case 'w':System.out.println("Sometimes a vowel");break;
default:System.out.println("consonant");break;
}
}
}
第5章 初始化与清理
1 构造器的名称必须与类名完全相同。所以"每个方法首字母小写"的编码风格并不适用于构造器。构造器也没有返回值
2 创建对象时new Xxxx()将会为对象分配存储空间,并调用相应的构造器。
3 不接受任何参数的构造器叫做默认构造器。
class Rock2{
Rock2(int i){
System.out.print("Rock "+i+" ");
}
}
public class HelloDate {
public static void main(String[] args){
for(int i=0;i<8;i++)
new Rock2(i);
}
}
4 方法重载指函数名一样,但参数类型,参数个数或参数顺序不一样。返回值有无及其类型不作为判断
class Tree {
int height;
Tree(){
System.out.print("Planting a seedling");
height = 0;
}
Tree(int intialHeight){
height = intialHeight;
System.out.print("Creating new Tree that is "+height+" feet tall");
}
void info(){
System.out.print("Tree is "+height+" feet tall");
}
void info(String s){
System.out.print(s+": Tree is "+height+" feet tall");
}
}
5 如果你写的类中没有构造器,则编译器会自动帮你创建一个默认构造器。
6 通过关键词this返回当前对象的引用
class Rock2{
int i = 0;
Rock2 increment(){
i++;
return this;
}
}
7 构造器调用构造器,可以通过this。在构造器内使用this调用一个构造器,但却不能调用两个
class Rock2{
int petalCount = 0;
String s = "initial value";
Rock2(int petals){
petalCount = petals;
}
Rock2(){
this("a",1);
this(1); // error , 用this调用一个构造器,但却不能调用两个
}
Rock2(String s,int petals){
this(petals); // 等同于调用Rock2(int petals)
}
}
8 static函数里是没有this,在static函数的内部不能调用非静态方法,反过来可以。可以仅仅通过类本身调用static成员函数。
9 垃圾回收器只知道释放那些经由new分配的内存,而无法释放非new创建的特殊内存区域。Java允许在类中定义一个名为finalize()的方法来应对这种情况。
原理:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时才会真正回收对象占用的内存。
10 java里的对象却并非总是被垃圾回收。即对象可能不被垃圾回收。垃圾回收并不等于“析构”,垃圾回收只与内存有关。
11 java没有delete操作符,也没有析构函数
12 java不允许创建局部对象,必须使用new创建对象。
13 对于函数的局部变量如果没有初始化,java会报错。对于类的成员变量,如果是基本类型都会有一个默认初始值。
public class HelloDate {
boolean t;
char c;
byte b;
short s;
int i;
long l;
float f;
double d;
HelloDate reference;
void printIntialValues(){
System.out.println("Data type Initial value");
System.out.println("boolean: "+t);
System.out.println("char: "+c);
System.out.println("byte: "+b);
System.out.println("short: "+s);
System.out.println("int: "+i);
System.out.println("long: "+l);
System.out.println("float: "+f);
System.out.println("double: "+d);
System.out.println("HelloDate: "+reference);
}
public static void main(String[] args){
HelloDate d = new HelloDate();
d.printIntialValues();
}
}
输出如下,boolean默认值为false,char,byte,short,int,float,long,double都是0,引用为null。
Data type Initial value
boolean: false
char:
byte: 0
short: 0
int: 0
long: 0
float: 0.0
double: 0.0
HelloDate: null
14 类的成员变量指定初始化
public class HelloDate {
boolean t = true;
char c = 'x';
byte b = 45;
short s = 0xff;
int i = 99;
long l = 12;
float f = 3.12f;
double d = 31.455;
HelloDate reference;
}
15 也可以通过调用成员函数给成员变量提供初值
// 第一种类型
public class HelloDate {
int i = f();
int f(){
return 12;
}
}
// 第二种类型,一定要注意顺序,i要先被赋值才能给j的赋值操作做参数
public class HelloDate {
int i = f();
int j = g(i);
int f(){
return 12;
}
int g(int i){
return i*10;
}
}
16 在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
class Window{
Window(int marker){ System.out.println("Window("+marker+")");}
}
public class HelloDate {
Window w1 = new Window(1);
HelloDate(){
System.out.println("HelloDate");
w3 = new Window(33);
}
Window w2 = new Window(2);
void f(){ System.out.println("f()");}
Window w3 = new Window(3);
public static void main(String[] args){
HelloDate d = new HelloDate();
d.f();
}
}
17 无论创建多少个对象,静态数据都占用一份存储区域。static关键字不能应用于局部变量,只能作用于成员变量
class Bowl{
Bowl(int marker){
System.out.println("Bowl("+marker+")");
}
void f1(int marker){
System.out.println("f1("+marker+")");
}
}
class Table{
static Bowl bowl1 = new Bowl(1);
Table(){
System.out.println("Table()");
bowl2.f1(1);
}
void f2(int marker){
System.out.println("f2("+marker+")");
}
static Bowl bowl2 = new Bowl(2);
}
class Cupboard{
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
Cupboard(){
System.out.println("Cupboard()");
bowl4.f1(2);
}
void f3(int marker){
System.out.println("f3("+marker+")");
}
static Bowl bowl5 = new Bowl(5);
}
public class HelloDate {
public static void main(String[] args){
System.out.println("Creating new Cupboard() in main");
new Cupboard();
System.out.println("Creating new Cupboard() in main");
new Cupboard();
table.f2(1);
cupboard.f3(1);
}
static Table table = new Table();
static Cupboard cupboard = new Cupboard();
}
// 输出
Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)
类初始化的顺序是先静态变量,而后是非静态变量,再构造器。静态变量只会被初始化一次
18 对象的创建过程,假设有个名为Dog的类
- 即使没有显式地使用static关键字,构造器实际上也是静态方法。因此,当售出创建类型为Dog的对象时,或者Dog的静态方法/静态变量首次被访问时,java解释器必须查找类路径,以定位Dog.class文件
- 然后载入Dog.class,有关静态初始化的所有动作都会执行。因此静态初始化只在Class对象首次加载时进行一次
- 当new Dog()创建对象时,首先将在堆上为Dog对象分配足够的存储空间
- 这块存储空间会被清零,自动地将Dog对象中的所有基本类型数据都设置为默认值,而引用设置为null
- 执行所有出现于字段定义处的初始化动作
- 执行构造器
19 Java允许将多个静态初始化工作组织成一个静态块,如下
class Cup{
Cup(int marker){
System.out.println("Cup("+marker+")");
}
void f(int marker){
System.out.println("f("+marker+")");
}
}
class Cups{
static Cup cup1;
static Cup cup2;
static {
cup1 = new Cup(1);
cup2 = new Cup(2);
}
Cups(){
System.out.println("Cups()");
}
}
public class HelloDate {
public static void main(String[] args){
System.out.println("Inside main()");
Cups.cup1.f(99);
}
}
//输出
Inside main()
Cup(1)
Cup(2)
f(99)
20 非静态的代码块
class Cup{
Cup(int marker){
System.out.println("Cup("+marker+")");
}
void f(int marker){
System.out.println("f("+marker+")");
}
}
class Cups{
Cup cup1;
Cup cup2;
{
cup1 = new Cup(1);
cup2 = new Cup(2);
System.out.println("cup1 & cup2");
}
Cups(){
System.out.println("Cups()");
}
Cups(int marker){
System.out.println("Cups("+marker+")");
}
}
public class HelloDate {
public static void main(String[] args){
System.out.println("Inside main()");
new Cups();
System.out.println("new Cups() completed");
new Cups(2);
System.out.println("new Cups(2) completed");
}
}
//输出
Inside main()
Cup(1)
Cup(2)
cup1 & cup2
Cups()
new Cups() completed
Cup(1)
Cup(2)
cup1 & cup2
Cups(2)
new Cups(2) completed
21 数组定义,有两种方式,定义时编译器不允许指定数组大小。数组定义只是拥有了对数组的一个引用,还没有给数组对象本身分配任何空间。初始化时会给数组创建相应的存储空间。
int[] a1 // ok
int a2[] // ok
int[4] a3 // error,不能指定数组大小
int a4[5] // error,不能指定数组大小
22 数组的初始化
int[] a1 = {1,2,3,4,5};
int[] a2;
a2 = a1;
int[] a3 = new int[4];
23 数组都有一个固定成员,length,表示数组个数。数组越界,java会报错。但C/C++不会报错。数组的大小可以在运行时确定
int[] a;
Random r = new Random(45);
a = new int[r.nextInt(20)];
System.out.println("length of a = "+a.length); // 查看个数
System.out.println(Arrays.toString(a)); // 查看数组内容
24 如果定义一个非基本类型的数组,那么只是创建了一个引用数组,并且直到通过创建新的Integer对象,并把对象赋值给引用,初始化才算结束。
// 第一种类数组初始化
Integer[] b1 = {
new Integer(1),
new Integer(2),
3
};
// 第二种类数组初始化
Integer[] b2 = new Integer[]{
new Integer(3),
new Integer(4),
3, // 最后这个逗号可选
};
Random rand = new Random(47);
// 第三种类数组初始化
Integer[] a = new Integer[rand.nextInt(20)];
for(int i=0;i<a.length;i++)
a[i] = rand.nextInt(500);
System.out.println("length of a = " + a.length);
System.out.println(Arrays.toString(a));
25 int k[] = {1,2,3 , }; 最后的一个逗号是可选项,可以没有也可以有
26 可以创建以Object数组为参数的函数,如下
class A{}
public class HelloDate {
static void printArray(Object[] args){
for(Object obj:args)
System.out.print(obj+" ");
System.out.println();
}
public static void main(String[] args){
printArray(new Object[]{ // 不同类型变量组成的数组
new Integer(47),new Float(4.13),new Double(4.333)
});
printArray(new Object[]{"one","two","three"});
printArray(new Object[]{new A(),new A(),new A()});
}
}
//输出
47 4.13 4.333
one two three
c02.A@6a998c1 c02.A@686baa51 c02.A@747ae165
27 在Java SE5中,加入了可变的参数列表,即数据类型,后面紧跟着省略号,即 function( T . . . args ) ,省略号必须是三点,
T可以使用任何类型的参数,包括基本类型。
static void printArray(Object... args){ // 可变参数列表
for(Object obj:args)
System.out.print(obj+" ");
System.out.println();
}
public static void main(String[] args){
printArray(34,34.1f,12.333); // 可以同时传多个参数
printArray(new Integer(4),new Float(3.423),new Double(11.1));
printArray(new A(),new A(),new A());
printArray("one","two","ok");
printArray(new Integer[]{1,2,3});
printArray((Object[])new Integer[]{1,2,3}); // 可以传数组
printArray(); // 可以是空的参数
}
static void f(int required,String... args){ // 可变参数列表
System.out.print("required: "+required+" ");
for(String s:args)
System.out.print(s+" ");
System.out.println();
}
public static void main(String[] args){
f(1,"one");
f(2,"two","three");
f(0);
}
static void f(Character... args){ // 可变参数列表
System.out.print(args.getClass());
System.out.println("length: "+args.length);
}
static void g(int... args){ // 可变参数列表,数据类型可以是基本类型
System.out.print(args.getClass());
System.out.println("length: "+args.length);
}
public static void main(String[] args){
f('a');
f();
g(1);
g();
System.out.println("int[]: "+new int[0].getClass());
}
//输出
class [Ljava.lang.Character;length: 1
class [Ljava.lang.Character;length: 0
class [Ilength: 1
class [Ilength: 0
int[]: class [I // [ 表示数组,I表示基本类型int
函数有了可变参数,编译器会为其自动填充数组,这个参数在函数内就是一个数组类型。
28 在Java SE5添加了enum关键字,由于枚举类型的实例是常量,所以它的具名值都是大写字母表示。
public enum Spiciness{
NOT,MILD,MEDIUM,HOT,FLAMING
}
public static void main(String[] args){
Spiciness howHOT = Spiciness.MEDIUM;
System.out.println(howHOT); // 输出MEDIUM
for(Spiciness s : Spiciness.values())
System.out.println(s+" , ordinal "+s.ordinal());
}
输出:
MEDIUM
NOT , ordinal 0
MILD , ordinal 1
MEDIUM , ordinal 2
HOT , ordinal 3
FLAMING , ordinal 4
在创建enum时,编译器会自动给添加一些特性,比如添加toString(),ordinal()和values()等函数