Java编程思想读书笔记

此笔记仅仅是作者的阅读此书时,发现自己错误认识和不牢固的知识,用来完善作者的知识框架,其列出重点不具有参考性,代码也是为了省工作量,简写代码,能看懂即可,语法并不规范

第一章 对象导论

第二章 一切都是对象

2.1 用引用操控对象

java语言中操作内存的方式是通过“引用”,因为java中一切可以看做对象,而操作对象只需要对象的一个引用。

例如

String s为 String的引用

**注意:**它只是一个地址,并没有分配任何的存储空间,所以现在向s发送信息(调用 s )会出现空指针错误。

因此安全的做法是在创建这个引用是同时为它初始化

String s = "aadf";

String的初始化可以通过 “字符串” 来实现,还有其他的基本数据类型初始化方式也是不相同的,这是Java语言的一个特性,但是一般对象的初始化不是这样的。

2.2 必须由你创建对象

通过new关键字来创建对象,new可以理解为“给我一个新的对象”。

通常我们创建一个引用就得和一个对象关联起来。这时候通过new出一个对象就可以和引用关联起来。

例如:

Stirng s = new String("aadf");

new 不仅表示给我一个新的字符串,也给出了如何产出这个字符串的信息(String("aadf")为构造方法)。

构造方法后边会涉及到,现在看不懂可以略过,只要记住 这个语句会有什么信息即可。

2.2.1 对象存储到什么地方

在计算机中有地方可以存储数据分别是 寄存器、堆栈、堆、常量存储、非RAM存储

**寄存器:**位于处理器内部、数量少,是最快的存储器,不能直接控制,俗称内存。

**堆栈:位于通用RAM(随机访问存储器)中,读取速度仅次于寄存器,可以用堆栈指针进行控制。 堆栈指针向下移动分配内存,向上移动释放内存。通常存储一些对象的引用,**但是对象 不存放在这里。

**堆:**一种通用内存池,位于RAM区 ,用于存储所有的Java对象,当 new 一个对象时,会自动在 堆中进行存储分配,编译的时候编译器不需要知道存储的数据需要存活多长时间,会有 Java的垃圾处理机制去处理一些不需要用的对象。

**常量存储:**常量值直接放在代码块中,他们永远不会被改变,这样做非常安全。

非RAM存储:主要是对数据的存储,数据的持久化和持久化对象。 JDBC

2.2.2 基本数据类型

有一些基本类型的对象是非常小的简单的变量,这些对象存放在堆中往往效率不是很高,所以把这些基本类型的对象存放在堆栈中。基本类型对象的不用new关键字来声明。

基本数据类型

基本类型 大小 最小值 最大值 包装器类型
boolean - - - Boolean
char 16-bit Unicode o Unicode 2^16 -1 Character
byte 8 bits -128 +127 Byte
short 16 bits -2^15 +2^15 -1 Short
int 32 bits -2^31 +2^31 -1 Integer
long 64 bits -2^63 +2^63 -1 Long
float 32 bits IEEE754 IEEE754 Float
double 64 bits IEEE754 IEEE754 Double
void - - - Void

基本类型创建对象和初始化的方法:

例如:

boolean  b = true;
// boolean类型所占的空间大小没有给出,所以只取字面值 true和falseint i = 1;chart c = 'a';double = 3.14;

每个类型都用它们对应的包装器类型,这可以让基本类型的对象通过包装器类型创建在堆中,来表示对应的基本类型。

例如:

char c = 'x';
Character ch = new Character(c);
//也可以这样用
Character ch = new Character('x');
//java 5开发的自动装包功能,基本类型和对应的包装类型可以互相转换
Character ch = 'x';
char c = ch;

高精度数字

Java中提供了两个高精度计算的类,他们属于包装类,但是没有对应的基本类型,不过包含的方法和操作与对基本类型所执行的操作相似

BigInteger 支持任意精度的整数,在运算中可以准确表示任意大小的整数值,而不会丢失信息
BigDecimal 支持任意精度的定点数,

Java数组

创建一个Java数组对象其实是创建了一个引用,并且引用的默认值为null

2.3 永远不要销毁一个对象

作用域

Java的作用域是用{}的位置决定的,基本类型只能再其作用域中调用,而对象直至销毁才消失,但是对象的引用只能在作用域中有效。

例如:

{String s = new Stirng("a string");
}//当出了{}的作用域,s引用就无效了,但是 new String()的这个对象不会消失还存在堆中。
2.4 创建新的数据类型:类

通过 class关键字创建新的数据类型:‘类’ ,一个类通常包含字段和方法

字段可以是基本类型,也可以是其他自定义的类,通过new 关键字可以创建这个类的对象。

每个对象都有存储字段的空间,普通字段不能再对象间共享

对象访问成员属性的方法 “对象名.属性名”

例如:

Class Hhh {int i;
}Hhh data = new Hhh();
data.i  //访问对象的属性i

基本成员的默认值

若类的某个成员为基本数据类型,即使没有被初始化,Java也会给一个默认值,但是不适用与局部变量,局部变量必须初始化。

基本类型 默认值
boolean false
char ‘\uoooo’(null)
byte (byte)0
short (short)0
int 0
long 0L
double 0.0f
2.5方法、参数和返回值

方法的基本组成包括:名称、参数、返回值、和方法体。返回类型描述的是在调用方法后返回的值。参数列表给出了要传给方法的信息的类型和名称。Java中方法通过对象来调用。

方法名和参数列表唯一地标识出某个方法

ReturnType methodNmae(/*Argument list*/){/*Method Body*/
}
int x = a.f();

通过a对象调用f()方法,把返回值传给x;这种行为叫做发送消息给对象, 消息是f(),对象是a

f()的返回类型必须是 int 类型

int storage(String s){return s.length() * 2;
}

return关键字的作用:

1.此方法已经执行完,要离开此方法

2.返回 return关键字后面的数据

2.6 构建一个Java程序
2.6.1 名字可见性

Java中采用域名反转的方式来避免命名冲突。

import关键字来导入一个包,也就是类库

2.6.3 static关键字

在Java中只有通过new创建对象才能分配存储空间,调用方法,这样两个问题解决不了,

  1. 只想为某特定域分配单一存储空间,而不考虑要创建多少个对象
  2. 希望某个方法不于包含他的类的任何对象关联在一起,也就是说即使没创建这个对象也能调用方法

通过static关键字就可以实现这两个需要。

当声明一个事物是static时,就意味着这个域或方法不会与包含他的那个类的任何对象关联在一起

类方法:带有static关键字的方法

类数据:带有static关键字的成员变量

其可以通过类名直接调用,也可以通过对象调用

class StaticTest{static int i = 47;static void test(){}
}StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();st1.i=10;   //通过对象名调用静态成员
StaticTest.test(); //通过类名调用静态方法

注意在这里st1.i 和st2.i指向的是同一个存储空间,这也就是static关键字开辟出来的空间它是唯一的,无论创建多少个对象,它都是唯一的。

2.7 你的第一个Java程序

java.lang是一个特定的包,它会被自动导入每一个Java文件中

2.7.1 编译和运行

想要编译和运行Java程序就必须需要Java开发环境 也就是JDK,切记安装完后要配置环境变量

2.8 注释和嵌入式文档

Java有两种注释风格:多行注释和单行注释

/*
*多行注释
*
*///单行注释
2.8.1注释文档

Java通过javadoc作为提取注释的工具,它采用就Java编译器的某些技术,查找程序内的特殊注释标签,不仅解析由这些标签标记的信息,也将毗邻注释的类名或方法抽取出来,生成注释文档。

2.8.2 语法

javadoc命令只能在 /**注释中出现 以 */结尾,使用方式有两种:嵌入HTML或者使用文档标签,独立文档标签:以@字符开头,且要位于注释最前面,行内文档标签 则可以出现在javadoc注释的任何地方,也是以@开头 但要在{}中。

注意javadoc注释只能为public 和protected 成员进行文档注释。private 不进行提取

2.8.4 常用标签示例
/**
*@author
*@param
*@return
*/

其他看书 34页

2.9 编码风格

类采用大驼峰式:所有单词首字母大写

变量和方法采用小驼峰式:第一个单词小写,其余单词首字母大写

第三章 操作符

3.4.1 方法中别名的问题
class Letter{char c;
}public class PassObject{static void f(Letter y){y.c ='z';}public static void main (String[] args){Letter x = nwe Letter();x.c = 'a';print(x.c);   //结果为  af(x);print(x.c); //  结果为  z}
}

在调用方法时,给方法传递参数只是传递了 对象的一个引用 x ,也就是执行了这个操作

y = x;

所以 到了方法里面y操作的还是具体的 Letter 对象 ,将其改变也就把原来的值给抹去了。

3.5 算数操作符

这里学到了一个 Romdom类 产生随机数的类

Random rand = new Random(47);

若创建对象时没有传递参数那么Java就会把当前时间作为产生随机数生成器的种子,并由此生成器每一次执行都会产生不一样的输出。

种子(用于随机数生成器的初始化值,随机数生成器对于特定的种子值,总是产生相同的随机数序列)

通过调用Random对象 程序可以生成不同类型的随机数字。只需调用方法

nextInt()、nextFlout()或nextDouble();

注意传递给nextInt()的参数设置了所产生的随机数的上线,而其下线为0,为了排除0可以执行+1操作。

3.6 自动递增和自动递减

这里主要针对前++和后++进行巩固理解(–相同) 可以改变操作数的运算

++a(–a) 或先执行运算 在生成值

int a=1;
int x = ++a;
/*
*拆分
*a=a+1;
*int x=a;
*/

a++(a–) 先生成值 在运算

int a = 1;
int x = a--;/**
*拆分
*int x = a;     -- x=1
*a=a-1          -- a=0
*/

记忆口诀:运算符在前边就先运算在生成值,值在前边就先生成值在运算。

如果还不懂在看书 43页

3.7 关系操作符

这里对 == 和equals()方法做理解巩固

public class Equivalence{main(){Integer n1 = new Integer(47);Integer n2 = new Integer(47);print(n1 == n2);   //结果为falseprint(n1 != n2);   //结果为ture}
}

为什么会出现这种结果呢?

因为 == 和 != 比较的是引用

引用在堆栈中的它只是一个地址,来指向 在堆中创建的对象。及时对象里的内容相同,

但是 n1和n2 是两个引用 所以他们一定不相等!!

注意 :基本类型 可以用 == 和 != 来比较内容

因为基本类型是创建在堆栈中的。

比较两个对象的内容–equals()

public class Equivalence{main(){Integer n1 = new Integer(47);Integer n2 = new Integer(47);//      print(n1 == n2);   //结果为false//       print(n1 != n2);   //结果为tureprint( n1.equals(n2));   //结果为 true}
}

Integer作为int的包装类,里边已经写好equals方法比较数据。

但是自己创建类时会出现这种情况

class Value{int i;
}
public class EqualsMethod2{main(){Value v1 = new Value();Value v2 = new Value();v1.i=v2.i=100;print(v1.equals(v2));    //结果为 false}
}

因为equals 默认的行为是比较引用 当我们定义一个类时,若需要用到equals() 我们需要在自己的类中覆盖此方法。

但是大多数的Java类库已经实现了equals方法来比较内容,不去比较引用。

3.8 逻辑运算符

短路现象:在一个逻辑语句中,如果执行到一半就能确定这个逻辑语句的结果,那么剩下的部分就不用执行了。

直接常量

直接常量的后缀字符表示了常量的类型,L、l带表long,F、f带表float。

十六进制适用于所有的整数类型 以前缀0x(0X)+ 0-9+a-f来表示

int i1 =0x2f;   //float类型   16进制数
Integer.toBinaryString(i1); //显示i1的二进制形式
3.9 指数计数法
float i = 1.39e-43f;  //这是Java中指数技术的方法 f代表类型

e= 2.718

但是在Java设计中 e=10的幂次

如 1.39e^-13 = 1.39*10^-13

3.10 按位操作符

按位操作符 操作整数基本数据类型的单个比特 也就是 二进制数的一位。

按位与操作&:两位都为1 结果输出1 其余输出0

按位或操作|:两位都为0 结果输出0 其余都是1

按位异或^:两个输出位相同输出 0,不同输出1

按位非操作~:只对一个操作数作用,取反。若输入0则输出1。

注意:对于布尔值结果来说,按位按位操作符与逻辑操作符具有相同效果,但是不同的是按位操作符不会 出现短路情况,也就是说会及时布尔表达式已经确定最后的结果,也会执行完最后一句。

3.11 移位操作符

移位操作符:针对处理基本数据类型的整数类型,当对char\byte\short进行移位操作的时候回自动转换为 int类型。

左移位操作符<<:按照操作符右侧指定的位数对操作符左边的操作数向左移动,在低位自动补0.

3<<2    //将3的二进制数左移两位0000 0011  左移两位    0000 1100   转换为10进制     12

在没有溢出的情况下,对于正数和复数,左移一位都相当于乘以2的1次方,左移n位相当于乘以2的n次方。

有符号右移操作符>>:按照操作符右侧指定的位数对操作符左边的操作数向右移动,如果该操作 数为正数,则在高位补0,如果为负数则在高位补1。

11>>2 //将11右移2位0000 1011 右移两位  因为11为正数所以高位补0 ---  0000 0010  转换为10进制 2

右移一位相当于除以2,右移n位,相当于除以2的n次方。 除不尽就截断。

无符号右移操作符>>>:无论操作数的正负都在高位补0 ,对于正操作数和右移操作没有区别, 对负操作数有区别。

移位操作符还可以与赋值操作一起使用:>>= \ >>>= \ <<=

​ 先进行移位操作,在赋值给左侧变量

3.13 字符串操作

+、+= 操作字符串时可以拼接字符串,

如果表达式以一个字符串起头 那么后续操作数都得是字符串类型的

int a =1;
int b =2;
int c =3;
print("aaa"+a+b+c);
//输出结果为 aaa123;  而不是aaa 6

在Java中会自动把“aa”引号中字符序列设置为字符串类型,

3.15 类型转换符

在Java中有自动类型转换机制,将整数赋值给float类型变量,会将int自动转换成float类型,

也可以进行强制类型转换

folat i = 3;
double a =(double) 3;  //强制类型转换

窄化转换:范围大的类型转换为范围小的类型,这样有可能丢失数据。是危险的所以要显式转换。

扩展转换:范围小的类型转换为范围大的类型,这样操作是安全的,所以不用显式转换。

截尾和舍入

问题:当21.9 这个 float或double类型的数据转换为int类型是 21还是22呢

答案是21 ,Java中采取截尾的操作,也就是左取整。

舍入:如果想要得到四舍五入的结果 需要中行 java.lang.Math中的round()方法。

double d = 0.7;
print(Math.round(d));
//结果为 1

提升

在Java中对基本数据类型进行算数运算或者按位运算的时候比int类型范围小的类型会自动转换成int类型来进行运算。

通常表达式中操作数最大的数据类型,为结果的数据类型

double*folat = double

第四章 控制执行流程

4.3 迭代
  • while

    while(Boolean -expression){statement  //循环体
    }
    
  • do -while

    do{statement //循环体
    }
    while(Boolean-expression)
    

    while和do-while的区别:do-while中的语句至少会执行一次。

  • for

    for(initialization; Boolean-expression; step){statement //循环体
    }initialization //初始化
    Boolean-expression //布尔表达式
    step //步进表达式
    
4.3.3 逗号操作符
for(int i=0,j=i+10;i<5;i++,j=i*2){print("i="+i+"j=+j");
}

在for内可以定义多个变量,用逗号隔开,但是必须是相同类型的变量。

定义多个变量只能在for循环中使用,其他不能使用,也就是逗号操作符,只能在for循环中使用。

4.4 Foreach语法

主要用于遍历数组和容器

for(float x:f){     //遍历f,x代表元素,将容器f中的每一个元素赋值给xsout(x);
}
4.5 goto

Java中没有goto语句,但是保留了goto关键字。,但是Java也有可以完成跳转的操作,

那就是利用 break、continue 和标签来完成

label1:
outer-iteration{inner-iteration{//...break;//...continue;//...continue label1; (1)// 中断内部以及外部迭代,直接转到label1处重新进入迭代//...break label1;   (2)//终端所有迭代,直接转到label1处,但并不重新进入迭代}
}
4.6 continue break return

一般的continue会退到内循环的开头,并继续执行

带标签的continue会达到标签的位置,并重新进入紧接在那个标签后面的循环。

一般的break会跳出当前循环

带标签的break 会中断并跳出标签所指的循环。不在进行迭代。

重点:在Java里需要使用标签的唯一理由就是因为有循环嵌套的存在,而且想从多层嵌套中break或continue。

return有两个作用:一是指定一个方法返回什么值,另一方面结束方法,返回值。

第五章 初始化与清理

5.1 用构造器确保初始化

在Java中初始化和创建是绑定到一起的,在创建对象时编译器就得知道要调用哪个构造器进行初始化。

不接受任何参数的构造器叫做默认构造器,也叫无参构造器

带参数的叫做有参构造器

注意:构造器的名称必须与类名相同

new Rock();  //在创建对象时将会为对象分配存储空间,并调用相应构造器。
5.2 方法重载

当我们不想用默认构造器进行初始化时,构造器就要强制进行方法重载,因为构造器的名字必须和类名相同,通过参数列表来区分到底是调用哪个构造器。

区分重载方法:

重载方法具有相同的方法名,

  1. 通过参数类型列表区分,每一个方法都有独一无二的参数类型列表。

  2. 通过参数顺序不同,可以区分重载方法。

  3. 已返回值来区分重载方法有两种情况

    int f();

    void f();

    如果有明确的语句可以让编译器判断出语义,那么就能正确调用

    例如: int x =f(); 编译器是可以知道需要调用 int f();

    但是 如果只是需要执行这个方法,但是不考虑方法的返回值,这种情况编译器就无法知道调用哪个方法了

    例如 : f();

涉及基本类型的重载:

当参数为基本类型时,如果传入的参数类型,小于方法中声明的形式参数类型,那么实际数据类型就会被提升。

当传入的是char类型参数,如果没有接受char类型的方法,那么char会直接转换成int类型。

如果传入参数类型大于形式参数类型,那么就需要进行窄化处理。也就是强制类型转换。

5.3 默认构造器

如果在类中没有创建任何构造器,那么在创建对象的时候编译器会自动生成一个默认无参构造器,并且调用它,但是如果类中声明了任何构造器,编译器都不会在自动调用这个默认无参构造器。

5.4 this 关键字

当创建两个同一类型的对象a,b 并且对象a,b都调用同一个方法peel() 那么怎么知道当前执行的peel()方法是哪个对象调用的呢?

其实在调用方法时,编译器暗自把当前对象的引用作为第一个参数发送给方法,

peel(a,1)。

peel(b,2)。

这只是内部的形势,但是我们并不能这样书写代码。

如果在方法内部想要获得调用方法的对象,那么就可以通过this 关键字来获取。

this 表示调用方法的那个对象的引用。

在构造器中调用的构造器,可以通过this关键字来完成。

Flower(int petals){petals = 3;
}
Flower(){}
Flower(String s,int petals){this(petals);   //用this在构造器中调用构造器。// this();   但是只能调用一个构造器。
}

可以用this调用一个构造器,但却不能调用了两个构造器,此外必须将构造器调用放在第一行,否则会报错。

5.5 清理:终结处理和垃圾回收

学习finalize()的原理用途,以及垃圾回收器的工作流程

垃圾回收器回收无用对象占据的内存资源,但是只能回收由new分配的内存,那些非new的特殊内存则无法解决,为了应对这种情况Java运行在类中定义一个finalize()方法.

工作原理:垃圾回收机制准备好释放对象所占用的存储空间的时候,程序首先调用finalize()方法,来清理一些无法被垃圾回收机制回收的内存,并在下一次垃圾回收动作发生的时候,才会真正回收对象内存。

finalize()不是析构函数

  1. 对象可能不被垃圾回收
  2. 垃圾回收并不等于析构。

finalize()的用途何在

  1. 垃圾回收只与内存有关

所以跟垃圾回收有关的任何行为来说,他们也必须同内存及其回收有关。尤其是finalize()方法

finalize()方法 是为了释放通过new方式创建对象以外的方式为对象分配的内存。

例如通过类c 的 malloc()函数来分配存储空间。

无论是垃圾回收还是终结,都不会保证一定会发生,如果Java虚拟机(JVM)并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存的

垃圾回收器如何工作

在以前的编程语言在堆上分配对象的代价是十分高昂的。

Java的垃圾回收器对于提高对象创建速度,有明显效果,这意味这Java从堆上分配空间的速度可以和其他语言在堆栈上分配空间的速度相媲美

在Java虚拟机中,堆的实现截然不同,类似于一个传送带,由堆指针控制,在它工作时,一面回收空间,一面是堆中的对象排列紧密,堆指针就很容易移动到更靠近传送带开始处。也就避免了内存页面错误。通过紧密排列对象的方式,实现了一种高速的、无限空间可供分配的堆模型。

想更好的理解Java的垃圾回收技术,首先了解其他系统中的垃圾回收方式,引用计数是一种简单但速度很慢的垃圾回收技术。

引用计数原理:每个对象都含有一个引用计数器,当引用连接对象的时候引用技术+1,当引用置为null或离开作用域时,引用计数-1. 当一个对象引用计数为0,就释放其空间。

**缺陷:**当对象只见存在循环引用的时候会出现,对象该被回收,但是计数不为0的情况。

还有一种更快捷的方式:对任何活的对象,一定能追溯到其存活在堆栈或者静态存储区之中的引用。

如果从堆栈区和静态存储区遍历所有引用,就能找到所有活的对象。发现每个引用就必须找到它所引用的对象。然后时此对象所包含的所有引用,指导所有引用访问完为止,这样就解决了,交互自引用的对象组的问题。

在这种情况下JVM选择了一种 自适应的垃圾回收技术

至于如何找到所有存活的对象,这取决于JVM的实现。

停止–复制 先将程序停止,然后把活的对象复制到另一个堆,紧密排列,位于堆栈和静态存储区域的引用从旧地址指向新地址。

问题一:在于对内存的要求,首先得有两个堆,在两个堆之间来回复制,系统开销大,部分虚拟机会按需在内存中分出几块较大的内存块,在这些大块的内存之间发生复制。

问题二:当程序稳定运行的时候会产生极少的垃圾,这时候在进行复制操作,就会很浪费资源,这时候JVM就会从 停止—复制 模式 转变到 标记—清扫 模式,这种模式对前种情况相当慢,但是当你知道只会产生少量垃圾的时候就会相当快了

标记—清扫 模式的思路同样时从 堆栈和静态存储区域出发,遍历所有引用,找到所有的存活对象,给对象一个标记,等全部对象标记完成后,没有被标记的对象就会被执行清理操作。

同样标记清扫模式也是在程序暂停情况下才能进行的

Java虚拟机中,内存分配以较大的块为单位,如果对象足够大,就会独自占用块, 这样就会产生大量的复制情况,有了块之后,可以对块进行标记,每个块都有相应的代数,来记录它是否还存活。如果块在某处被引用,它的代数就会增加,复制的时候也不会进行复制,只是代数增加,内含小型对象的块则被复制整理。

终结条件

用finalize()来验证终结条件,

当一个对象可以被清理的时候,它因该处于某个状态,例如一个对象是打开文件,那么清理这个对象的时候,它就必须得是关闭状态,因为finalize是发生在清理之前的,所以可以用finalize来验证文件是否关闭。也就是这个对象是否应该被清理。

5.6 成员初始化

在Java中尽力保证所有的变量在使用前都能得到恰当的初始化。

如果类中的数据成员是基本类型,如果不对数据成员初始化,编译器会自动为起设置初值,进行初始化。

boolean = false

char 【】

其余的为 0

应用初值 为 null

5.7 构造器初始化

可以用构造器初始化,把初始化信息写入构造器中,但是自动初始化还是会进行的。它将在构造器调用之前进行。

初始化顺序:

变量定义的顺序决定了初始化顺序。

若基本数据类型定义在方法之间,也会在调用方法之前进行初始化。

静态数据的初始化:这里主要知道,静态数据默认初始化和基本数据类型一样,还有类加载时,静态数据成员和非静态数据成员的初始化顺序是先静态后非静态

静态数据都只占一份存储区域,不能应用于局部变量。

如果静态数据在创建的时候没有进行初始化,那么就会获得基本类型初始化标准初值。

静态成员变量值初始化一次。

在类加载时候初始化的顺序:先静态对象后非静态,且静态对象只初始化一次

例如

class Bowl{Bowl(int marker){print("Bowl("+marker+")");}void f1(int marker){print("f1("+marker+")"); }
}
class Table{static Bowl bowl1 = new Bowl(1);Table(){print("Table()");blow2.f1(1);}void f2(int marker){print("f2("+marker+")"); }static Bowl bowl2 = new Bowl(2);
}class Cupboard{Bowl bowl3 = new Bowl(3);static Bowl bowl4 = new Bowl(4);Cupboard(){print("Table()");blow4.f1(2);}void f3(int marker){print("f3("+marker+")"); }static Bowl bowl5 = new Bowl(5);
}class Test{main(){print("Creating new Cupboard() in main");new Cupboard();print("Creating new Cupboard() in main");new Cupboard();table.f2(1);cupboard.f3(1);}static Table table = new Table();static Cupboard cupboard = new Cupboard();
}/*
当编译器编译Test类时,会先加载静态数据成员,在本例中会先加载 table和 cupboard,
table声明在cupboard之前,所以会先加载 Table类,而Table类中又有静态数据成员bowl1和bowl2,所以再先加载Bowl类两次,控制台打印出Bowl(1);
Bowl(2);Table类的静态数据成员初始化完成。因为在创建Table类的对象所以要执行Table的构造器。控制台接着打印:Table()
f1(1)到这里Test类的table数据成员加载完毕,接下来加载cupboard数据成员
在创建cupboard对象之前,编译器需要加载Cupboard类,所以会先初始化数据成员,bowl3和bowl4,bowl5
本着先初始化静态在初始化非静态的原则,虽然bowl3声明在bowl4之前,但是还是会先初始化bowl4
控制台打印:Bowl(4);
Bowl(5);
Bowl(3);然后在去执行构造器来初始化对象,控制台打印Cupboard()
f1(2)到这里Test类的数据成员全部加载完毕,所以要执行main方法了控制台打印Creating new Cupboard() in main之后下一句会创建一个Cupboard对象,继续加载Cupboard类控制台打印Bowl(3)
Cupboard()
f1(2)这里之所以没有像第一次一样打印出 bowl4 和 bowl5的信息,是因为 bowl4和bowl5是静态成员,静态数据是单独开辟出一个空间开存储,静态成员只初始化一次,当第二次创建Cupboard()类时,则不再初始化静态数据成员
紧接着控制台打印Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)*/

在这里总结一下对象创建的过程:假设有个名为Dog的类

  1. 即使没有显性地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog对象时,或者Dog类的静态方法\静态域首次被访问时,Java解释器必须查找类路径,来定位Dog.class文件。
  2. 然后载入Dog.class文件,有关静态初始化的所有动作都会执行,因此,静态初始化只在Class对象首次加载的时候进行一次。
  3. 当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
  4. 这块存储空间会被清零,这就会自动地将Dog对象中的所有基本类型数据都设置成了默认值,引用设置成null
  5. 执行所有出现于字段定义处的初始化动作。 int x =6
  6. 执行构造器。
5.8 数组初始化

本模块主要学习数组是什么,以及数组的集中初始化形式

数组是相同类型的,用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。

int[] a;
int a[];
//a 为标识符。现在只是定义了一个数组的引用,

编译器不允许指定数组大小,因为现在定义的只是一个数组的引用,编译器已经为这个引用分配了足够的存储空间,而对数组对象还没有分配任何空间,所以为了给数组对象本身创建空间,我们必须执行初始化操作。

数组的初始化可以在程序任何一个地方,但是有一种必须在数组声明处,就是用{}括起来的值。

int[] a1 = {1,2,3,4,5};
//在这种情况下,会自动分配空间给数组对象,相当于执行new操作,由编译器负责。

还可以将一个数组赋值给另一个数组

int[] a2;
a2 = a1;
//其实就是将a2 引用也指向了a1 引用指向的地址。
//无论操作a1还是a2 他们的值会一起变,因为他们指向的是同一个地址。

数组对象还有存在一个成员,通过这个成员可以获取数组中元素个数。这个成员是length

最后一个元素的数组下标对应 length-1

第一种初始化方式是我们知道我们的数组中需要多少个元素,倘若我们不知道数组中有多少个元素那么就用到了第二种初始化方式

int[] a;a=new int[N];
// N代表 数组元素个数。int[] a = new int[N];    //尽量这样声明

这种初始化方式可以在程序任何地方进行初始化操作。

注意,如果创建了一个非基本类型数组,其实是创建了一个引用数组,这个数组里全部存储的是引用。

以Integer包装类为例

Integer[] a = new Integer[100];for(int i =0;i<=a.length;i++){a[i]=1;
}

当创建了一个Integer的数组的时候,数组里存储的只是 Integer类型的引用。

for里边的赋值操作才是讲引用数组指向指定地址,只不过这里用自动包装机制来创建的存储空间。

a[i]=1;

相当于

a[i]= new Integer[1];

当然也可用{}的形势来初始化对象数组

Integer[] a= {new Integer(1),new Integer(2),3
}

还可以在方法的参数列表中创建数组,这里在调用方法是就可以提供可替换参数。

public class DynamicArray{main(){Other.main(new String[]{"1","2","3"});}
}class Other{public static void main(String[] args){for(String s: args){sout(s+"");}}
}//out
1 2 3

可变参数列表

由数组可以实现可变参数列表

Class A{}
public varArgs{static void printArray(Object[] args){for(Object obj : args){sout(obj + "");}}public static void main(String[] args){printArray(new Object[]{new Integer(1),new Float(3.14),new                   Double(11.11)});printArray(new Objcet[]{"one","two","three"});printArray(new A(),new A(),new A());
}
}//Out
1 3.14 11.11
one two three
A@23jj3 A@324ks A@dsfai

可以看到print用Object数组作为参数,而Java中所有的类都继承于Object所以此方法可以处理任何类型的数据,因为在调用方法,传递参数时,参数会向上转型成为Object类型。

这样基本就用数组实现了一个可变参数列表。

在Java SE 5 中加入了这一特性,可变参数列表。

Class A{}
public varArgs{static void printArray(Object... args){ // 因为可变参数为Object 类型所以所有调用方法时,传递的参数都会变成Object类型。for(Object obj : args){sout(obj + "");}}public static void main(String[] args){printArray(new Object[]{new Integer(1),new Float(3.14),new                   Double(11.11)});printArray(1,3.14,11.11);printArray("one","two","three");printArray(new A(),new A(),new A());printArray((Object[])new Integer[](1,2,3,4));printArray();
}
}

可以看到与用数组实现可变参数列表相比,这里的 printArray()方法的参数 变为**Object... args 这就是可变参数,**

有了可变参数,在调用方法的时候就不用显示的去编写数组,编译器实际上会自己填充数组,

实现从元素到数组的转变,

但是如果传递的是数组,编译器会发现已经是一个数组了,则不会在执行任何转换

可以不传递任何参数给方法。这样转变后的数组大小为0。

当然可以设置任何数据类型的可变参数,包括基本类型,但就只能接受此类型的参数,上述为Object类型,所以可以接受任何类型参数。因为所有的类继承于Object类。

可变参数在方法重载中的坑。

public class OverloadingVarargs{static void f(Character... args){print("first");for(Character c:args){print("" + c);println();}}static void f(Integer... args){print("second");for(Integer c:args){print("" + c);println();}}static void f(Long... args){print("third");}main(){f('a','b','c');f(1);f(2,1);f(0);f();}
}//Out
frist a b c
second 1
second 2 1
second 0
third

在上述例子中传递参数调用方法,编译器会使用自动包装机制正确匹配重载的方法,

但是f(); 无参数调用的时候,编译器就无法知道调用哪个方法了

为了解决这个方法,可以在 方法中添加一个非可变参数来区别方法

public class OverloadingVarargs{static void f(char a,Character... args){print("first");for(Character c:args){print("" + c);println();}}static void f(int a,Integer... args){print("second");for(Integer c:args){print("" + c);println();}}
枚举类型

使用enum关键字

public enum Spiciness{NOT,MILD,MEDIUM,HOT,FLAMING
}//创建enum时,编译器会自动加一些特性,比如 toString()方法打印,ordinal()方法记录常量声明顺序。

switch 语句与enum 绝佳的组合。

Java编程思想 第四版 读书笔记巩固基础,完善知识框架。相关推荐

  1. Java编程思想第四版读书笔记——第九章 接口

    这章介绍了适配器设计模式和策略设计模式. 第九章  接口 接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法. 1.抽象类和抽象方法 public abstract void f(); 创 ...

  2. Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(二)之Introduction to Objects...

    The genesis of the computer revolution was a machine. The genesis of out programming languages thus ...

  3. Java编程思想(第4版)读书笔记——01

    1.面向对象程序设计(Object-oriented Programming, OOP) 2.程序运行时,对象有五个不同的地方可以存储数据: (1)寄存器 (2)堆栈 (3)堆 (4)常量存储 (5) ...

  4. Java编程思想第四版——第十五天

    2012-04-23 121-131/913 Java编程思想第四版--第十五天 5.5.3 终结条件 通常不能指望finalize(),必须创建其他的"清理"方法,并明确的调用它 ...

  5. Java编程思想第四版学习总结

    Java编程思想第四版学习总结 文章目录 Java编程思想第四版学习总结 第 1 章 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 实现方案的隐藏 1.4 方案的重复使用 1.5 继承:重 ...

  6. Java编程思想第四版第九章练习

    这一章讲的是接口, 其中抽象和C++中的纯虚函数特别相似,我会在Java编程思想专栏做完以后,专门写一篇文章来对比C++和Java的不同. 1.修改第8章练习9中的Rodent,使其成为一个抽象类.只 ...

  7. Quartz简介,java编程思想第四版pdf百度云

    Quartz是免费使用的,并根据Apache 2.0许可获得许可. Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目,完全基于 Java 实现.该项目于 2009 年被 ...

  8. JAVA编程思想第四版笔记 十、内部类

    十.内部类 10.1 创建内部类 可以将一个类的定义放在另一个类的定义内部,这就是内部类. public class Parcell {class Contents{private int i = 1 ...

  9. java编程思想第四版第十四章 类型信息习题

    fda dfa 第三题u package net.mindview.typeinfo.test4;import java.util.ArrayList; import java.util.Arrays ...

最新文章

  1. CommunityServer研习心得(转)
  2. 一身漏洞狂奔24年!人人都用的WiFi被曝重大漏洞,随时成为监控你的工具
  3. 在参加比赛之后一定要注意的事情
  4. Puppet SaltStack Chef Ansible
  5. 基本数据类型(dict)
  6. 不给糖果就捣乱,用Python绘制有趣的万圣节南瓜怪【华为云分享】
  7. CHR-6dm datasheet 中文翻译
  8. matlab自动交易系统 浏览
  9. IDea更改主题和部分修改主题方法
  10. zblock 结构_结构方块 - Minecraft Wiki,最详细的官方我的世界百科
  11. 为什么存png还有白色底_用photoshop保存透明背景的图片为png格式,为什么打开后是白色背景了?...
  12. 10种令人吃惊的方式你的日常生活中正在收集数据的大数据野兽
  13. 基于python的dlib库的人脸识别
  14. window7系统怎么给电脑安装
  15. 谷歌商店输入账号密码后 回到登录界面/闪退
  16. rtl8812驱动分析(二)
  17. MySQL--数据库、表基本操作
  18. FPGA驱动千兆以太网PHY但电脑只显示百兆
  19. php 正则表达式 环视,正则表达式教程五 —— 环视(零宽断言)
  20. 使用tesseract训练自己的字库提高识别率

热门文章

  1. Direct3D 11 总结 —— 4 绘制三角形
  2. USIM卡专区目录结构
  3. 掌财社:什么是低位十字星孕线形态?
  4. Poison社RPG,百花缭乱的补充资料
  5. qt客户端显示服务器发送的图片不显示,使用Qt将一系列图片通过网络发送到客户端动态显示...
  6. 启航——三十而立四十不惑的程序员
  7. mysql中in的用法详解
  8. 一个简单的后台管理系统
  9. 计算机专业主要学习什么
  10. 【量化投资】我实现了A股买卖点数量趋势图