第二章 一切都是对象
只有引用,无对象(记住,你能做到的只能是操作对象的引用,而非对象。)
如: Test a = b; 赋值的是引用,绝非对象(你没有机会操作对象)
2.2.2 java 基本类型 固定大小,与硬件平台无关。
char 16bits
java无sizeof
无 unsigned
boolean 仅能够赋值为 true或false 0,1 或者其他整数会报错。
基本类型具有的包装器类,使得可以在堆中创建一个对象,表示对应的基本类型
作用域:
{
int x=1;
{
int x=2;
}
}
C/c++中是可以的,但在java中上面写法是不允许的,java认为这样写会导致程序混乱。
java 中使用 术语“方法” 来替代 “函数”。
名为a的对象,消息是f()
a.f();
发送消息给对象。
传递参数: 方法的参数列表指定要传递给方法什么样的信息。
这些信息像java中的其他信息一样,采用的都是对象形式。
java中任何传递对象的场合,传递的实际上是引用。 传递: 对象 的 引用。 传参,本质传递的是对象。 通过引用来传递对象。
java 一切皆是对象。 操作对象 使用的是引用。
使用其他构件: 使用某一个特定的类, 使用import指示编译器导入一个包,即类库,java所有代码都必须写在类里。
大多时候使用与编译器附在一起的java标准类库,就不必写域名了。如 import java.util.ArrayList;
或更常用: import java.util.*;
static:
通常,创建类时,是在描述那个类的对象的外观和行为, 除非new创建对象,否则未获得任何对象。(java一切皆对象)
两种情形:
1.希望只分配单一存储空间。
2. 方法不与类的任何对象关联在一起。
使用static关键字:
把static放在定义前:
class Test{
static int i=1; // 可以将 字段或方法 设定为static。 代码生成了一个static字段。
int j;
}
尽管当static作用于某个字段时,肯定会改变数据的创建方式。
但,将static 作用于某个方法, 却仅仅是在不创建任何 对象的前提下就可以调用它。
类库:
请参考 jdk文档,即类库的说明文档。
有一个类会自动被导入到每个java文件中: java.lang
java.lang 有些什么呢?请参考 上面的jdk说明文档。
jdk文档左侧栏的上半部分是 包。
点击上半部分中 的java.lang.
左侧栏的下半部会显示 该包中的 : 接口,类,枚举,异常,错误。。。
文档(注释文档,说明文档):
javadoc 提取注释。
有一些注释语法:
1. 所有javadoc 都在“ /** ” 注释中。
2. 方式 有两种: ”嵌入式的html“ 或者 “文档标签”
注释的对象(元素):
类, 域, 方法。
//: Test.java
/** A class comment */
public class Test{
/** A field comment */
public int i;
/** A method comment */
public void f(){}
} ///:~
嵌入式html: 主要是为了 进行 格式化显示。
/**
* You can even insert a list:
*
* - Item one
*
- Item two
*
*/
文档标签:
分类:
独立文档标签
@see classname
@see fully-qualified-classname#name
产生的文档的显示效果 等同 HTML的超链接:
会在生成的文档中加入一个具有超链接的 “See Also”
行内文档标签
{@docRoot}
{@link package.class#member label}
用于行内,用label作为超链接文本,不用“See Also”
示例:
//: Test.java
/**
* @author xxx
* A example case for javadoc.
*/
/** @see TestOther */
public class Test
{
/** A field comment */
private int i;
/** A method comment */
public void fun(){}
}
类 Test
java.lang.Object
Test
public class Test
extends java.lang.Object
另请参阅:
TestOther
构造器概要
构造器 构造器和说明
Test()
方法概要
方法 限定符和类型 方法和说明
void fun()
A method comment
从类继承的方法 java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
构造器详细资料
Test
public Test()
方法详细资料
fun
public void fun()
A method comment
作业:
自己动手写存在的问题:
1. 仍然没有习惯类,在类的定义成员的地方,当成C函数似的,又定义了一个类。
类中能定义其他类的地方在方法中。
public class Test
{
class DataOnly
{
int i;
}
public static void main(){
DataOnly dao = new DataOnly();
dao.i = 5;
System.out.println("i="+i);
}
}
1. DataOnly 定义不能写在类成员的地方
2. println 打印时候,要写全类: dao.i , 这是C的坏习惯。忘记加类名。
一切都是对象:
相比C或c++,java是一种"面向对象"的设计语音。
对象是基本元素。 java就是对象。
用引用 操纵对象:
操纵内存的方式: 在java里,一切都被视为对象,但操纵的标识符实际上是对象的一个“引用”。
理解: 对象 - 引用。 对象存在于内存中,且只有一个。 对象的引用可以有多个。
对象存在在内存中。 多个引用来使用这块内存(对象)。
如想操纵一个String对象,则可以创建一个String 引用。
看来: 想操纵一个对象或内存的必要条件是 首先要有个引用:
String s; 所创建的只是引用,并不是对象。 对象离不开new。
理解了引用,对对象的创建就好理解了: new
第3章 操作符
静态导入: import static
http://kanpiaoxue.iteye.com/blog/1977862
静态导入,意味着 想导入类的函数,而不是类本身。
如果使用了静态导入,使用时就要省略包名,否则一样会报错的。即,静态导入不是import的超集,而是不同层次的导入。
import 导入: 使用时要带包名。
import static 导入: 使用时,不要写包名,否则报错。
操作符:
几乎所有操作符都只能操作基本类型。
例外是: = == != 这三个操作符可以操作所有的对象(错,其实是引用,怎么可能操作对象呢)。
String类还支持 + +=
”对象“ ”赋值“操作:
对一个对象进行操作时,真正操作的是对象的引用。
所以,将一个”对象“赋值给另一个”对象“,实际上,是将引用赋值给另一个引用。
可以这么认为,引用是作为一种类型存在的,对象是存在于内存中的。
所以,上面的赋值操作,准确的说(实际上)是 引用的赋值操作。 永远无法直接操作对象本身,只存在引用。
方法调用中的别名现象:
将一个对象传递给方法时,同样传递的是对象的一个引用。 (类似于指针)
AClassType a1 = new AClassType();
AClassType a2 = new AClassType();
System.out.println(a1 == a2);
我们知道,结果是false。
因为上面比较的是引用,很明显是两个不同的引用。
函数传参:
在C语言中,函数传参是复制参数的一个副本。
但是在java中,如果参数是对象,但实际上只是传递了一个引用(你永远无法直接操作对象本身,它在内存,垃圾回收机制也是扫描是否有引用。)
所以,在函数体中,如果修改了引用,是会修改函数之外的对象的。
Random 用法:
Random rand = new Random(47);
int j = rand.nextInt(100) +1;
随即数,注定了它不可能是个静态方法,所以,别忘记了 new一个Random() 然后再使用 刚才new的这个对象来产生随即数。
比较操作:
首先,仍然是那句话,你所直接操作的是对象的引用。
比较操作时,比较的也是引用 : ==
如果希望比较对象,而不是引用,那么你就只能自己来实现了,所以呢,既然是自己实现,那么一般是函数,equals。
总结: 直接比较是引用 ==
对象内容的比较 equals(需要自己去实现equals)
短路:
test() && test2() && test3();
找到了false就不再进行下去。
&& 操作本来就是 1 不起作用, 0 起作用。 如果找到了 0 ,就不再找
常量:
十六进制表示 适用所有整数类型 int long short等
编译器通常会将指数作双精度double处理
float f = 1e-43f; 后面的f是必须的。
第四章: 控制执行流程
迭代: while do-while for
while: 循环刚开始时, 会 计算一次 表达式的值; 在下一次迭代开始前 再次计算布尔表达式的值。
即,每次循环开始前,先计算布尔表达式的值。
do-while 和while的唯一区别是:do-while 中的语句 会至少执行一次。 即使,布尔表达式第一次就被计算为false。
for: 在第一次迭代前要进行初始化; 然后进行条件测试; 每次循环结束,执行一次步进。
空for循环: 里面是两个分号,而不是三个 亲!
for(;;){}
for语句的 初始化部分,可以拥有任意数量的 具有相同类型的变量定义。
foreach语法:
仍然是for,使用形式不同。
foreach语法用于数组和容器。表示不必创建int变量去对访问的序列进行技术,foreach自动产生每一项。
foreach是一个简洁的自动语法。 它的含义如上描述。即,使用foreach语法,就代表你希望按照上面的描述来访问序列的每一项。
f[10]....
for(float x: f)
System.out.print(x);
上面foreach语法,就表示 将自动对每一项动作。 它是一种简洁的表达语法。
而对于这种: for(int i=0; i<100; i++){}
foreach 语法将不起作用。 因为它并非是一个序列(数组或容器)对象,它只是一个数而已,无法产生foreach的含义,对序列的每一项。。。
foreach适用于一个对象表示的范围,而不是数值表示的范围。
无条件 分支: return break continue
break
用于强行退出,不再执行循环中剩余的语句。
continue
停止执行 当前的迭代,然后退回循环起始处,开始下一次迭代。
goto: java中没有goto!然而java能完成一些类似的操作。使用了相同的机制: 标签。
标签是后面有冒号的标识符,
label:
xxx:
abc:
标签起作用的地方是 刚好在迭代语句之前。
标签和迭代之间置入任何语句都不好。
在迭代开始之前 设置标签的理由是:
由于break;continue只中断当前循环,若随标签一起使用,可以跳到标签所在的地方。
关于递增表达式是否执行:
break 不管带不带标签都不执行递增 : i++
continue 与带不带标签也无直接关系: continue带标签,但若只是一层迭代即只是通过标签跳出了一层迭代,那么递增仍然会执行。(已测试)
可 是如果因为使用了标签,continue跳出了两层迭代,那么后面的迭代是不执行的。 (已测试)
import static com.xxx.tij.myutils.Print.*;
public class Test
{
public static void main(String[] args){
int i = 0;
outer:
// for(;;){
for(;;i++){
println("i="+i);
if(i ==2){
println("continue outer");
continue outer;
}
}
// }
/* int i=0;
outer:
for(;true;){
inner:
for(;i<10;i++){
println("i="+i);
if(i == 2){
println("continue");
continue;
}
if(i == 3){
println("break");
i++; //
break;
}
if(i == 7){
println("continue outer");
//i++;
continue outer;
}
if(i == 8){
println("break outer");
break outer;
}
for(int k = 3; k<5; k++){
if(k==3){
println("continue inner");
continue;
}
}
}
}*/
}
}
第五章 初始化 与 清理
函数重载: 构造器是要求java支持函数重载的一个原因。
this关键字:
函数: 发送消息给对象。
a.peel(1);
b.peel(2);
方法调用的内部形式:
Banana.peel(a, 1);
Banana.peel(b, 1);
编译器 暗自把 函数 所操作对象 的引用,作为第一个参数传递给peel()
由于这个引用是 编译器“偷偷” 传入的,所有没有标识符。但,有个专门的关键字: this。
通常(例外的情况是this还可用作构造器),this只能在 方法 内部使用,表示 调用方法的那个对象 的引用。记住,this是怎么来的:
是编译器暗自把 调用函数的对象的引用给当做函数的第一个参数传进来的,因为没有标识符,才叫做this,
当然,只能在 函数里使用了,表示的就是调用函数的那个对象的引用。
this使用:
1. return this;
2. 将自身 传递给外部方法: 即,在函数中调用外部函数,将自身作为参数传递给外部函数时,使用this
3. this.成员
4. 特殊: 构造器中调用构造器。
通常情况下this代表 函数中的 调用该函数的对象。 但是如果this后面带括号和参数。
那么就变成了构造器调用。
this();构造器的调用必须是 另一构造器调用 它,普通函数不能调用 this形式的构造器。
this形式的构造器调用,只能调用一次,且必须在起始处。
static方法: 是没有this的方法。
static方法不是面向对象的。它确实有些全局函数的含义, 如果在代码中出现大量static就要重新考虑设计了。
finalize 是随着垃圾回收器一起发生,而垃圾回收器不保证一定会发生,因此,不要指望finalize进行很及时的清理操作。
但是,一些对清理的时间没有要求,但是 清理条件可以在finalize中进行验证,即,希望只要finalize发生,就一定要先验证是否符合条件。
可以在finalize中进行,对象终结的条件进行验证:
public class Test
{
public static void main(String[] args){
Book book1 = new Book(1);
//book1.in();
for(int i=0; i< 10; i++){
new Book(i);
}
System.gc();
for(int j=0; j< 1000; j++){
new Book(j);
}
}
}
class Book
{
boolean out = false;
final int id;
public Book(int i){
out = true;
id = i;
}
void in(){
out = false;
}
protected void finalize(){
if(out){
System.out.println("error:"+id);
}
}
}
注: 即使调用System.gc()垃圾回收也不是一定执行,只要空间还充足。(已测试)
初始化:
局部变量,如果忘记初始化,编译器会报错提示: 可能尚未初始化变量。
类成员变量:
1. 可以不初始化,则基本类型会有默认值(其实是通过new时,将整个块清零得到的)
对象引用的初值是null
初始化:
1) 类成员可以不初始化,默认值
2)在定义类成员的地方赋值,这样每个类的对象,都含义相同的初值。
3)构造器初始化,相比(2)更灵活。
但是,构造器初始化不是必须的,因为编译器总会保证使用前,类的成员已经得到初始化 通过步骤(1)或(2)。 (2)(3)是非必须的。(1)是自动的。
成员变量初始化顺序:
变量定义的顺序决定了初始化的顺序,即使散布于方法定义之间。
变量总是在任何方法被调用前已经得到了初始化,包括构造器,这也同样证明了 构造器中的初始化不是必须的。
static静态变量初始化:
static关键字只能作用于 成员。
无论创建多少个对象,static域都只占用一份存储区域。 确切说的共享。
一个对象修改了它的static域,其他对象的static域也会变成修改后的。 所有static最好用于一改全改的情况。
静态成员在 类载入过程中初始化,且只进行一次初始化。
然后是普通成员进行初始化。
数组:(数组的使用 == 对象) 操作的同样是引用,而不能直接操作数组的值本身。
type[] array; 编译器不允许指定数组的大小。是因为,现在拥有的是数组的引用,非数组本身。
既然是引用,那么没有必要限定数组的大小。
数组初始化:
1) 特殊的初始化: 在定义数组的地方出现,由一对花括号括起来的值组成:
int[] a = {1, 2, 3, 4, 5}; 它仍然是new出来的。 还是那句话,因为这里操作的是引用 ,引用指向的内存是new的。
因为是引用,所以 可以给引用赋值: int[] a2; a2 = a1;
2) 由于数组的元素是new出来的。 动态创建的。 所以数组的大小可以在运行时指定: 如rand.nextInt(xx);
3) 同其他对象一样,最好在定义时 进行初始化: 写法:
new int[123]; 看来,数组这种“对象” 的定义形式是:(假设这里元素是int类型) int[xxx],则分配对象用 new int[xxx];
就像 class Test , 对象分配用 new Test();
数组类型,有其不同点,就是 分配了其自身的空间后,还要为其元素(它的元素可以是引用(引用指向的对象仍然需要重新分配空间,即你永远只能操作引用,对象仍然需要new),或基本类型)进行初始化。
4)初始化列表的两种形式:
第一种:上面提到过的(1)的形式:Integer[] a = {new Integer(1), 3};
第二种: Integer[] b = new Integer[] { new Integer(1), 3 };
就像上面提到过的1)的初始化列表的形式受限,它只能用于数组定义处。
可以在任何地方使用第二种形式。
数组,无论元素是基本类型还是对象,都有一个固有 成员,length。它不是函数。
可变参数列表:
关键是在传参时,要像可变参数列表。 第二种初始化列表的形式,一定程度上支持了可变参数列表。
java的可变参数列表,实现的基础是 上面的数组的 第二种初始化列表形式。
因此,可在可变参数列表的 函数中,使用 foreach进行迭代。(因为 java会把可变参数列表,实现为数组)
static void fun(Object... args){
for(Object e: args)
print(e);
}