`

Think in java (笔记一)

阅读更多

1、Java 采用三个显式(明确)关键字以及一个隐式(暗示)关键字来设置类边界:public,private,protected 以及暗示性的friendly。若未明确指定其他关键字,则默认为后者。这些关键字的使用和含义都是相当直观的,它们决定了谁能使用后续的定义内容。“public”(公共)意味着后续的定义任何人均可使用。而在另一方面,“private”(私有)意味着除您自己、类型的创建者以及那个类型的内部函数成员,其他任何人都不能访问后续的定义信息。private 在您与客户程序员之间竖起了一堵墙。若有人试图访问私有成员,就会得到一个编译期错误。“friendly ”(友好的)涉及“包装”或“装”(Package)的概念——即Java 用来构建库的方法。若某样东西是“友好的”,意味着它只能在这个包装的范围内使用(所以这一访问级别有时也叫作“包装访问”)。“protected”(受保护的)与“private”相似,只是一个继承的类可访问受保护的成员,但不能访问私有成员。继承的问题不久就要谈到。

 

 

 

2、几乎所有运算符都只能操作“主类型”(Primitives)。唯一的例外是“=”、“==”和“!=”,它们能操作所有对象(也是对象易令人混淆的一个地方)。除此以外,String 类支持“+”和“+=”。

 

 

 

3、赋值是用等号运算符(=)进行的。它的意思是“取得右边的值,把它复制到左边”。右边的值可以是任何常
数、变量或者表达式,只要能产生一个值就行。但左边的值必须是一个明确的、已命名的变量。也就是说,
它必须有一个物理性的空间来保存右边的值。举个例子来说,可将一个常数赋给一个变量(A=4;),但不可
将任何东西赋给一个常数(比如不能4=A)。
对主数据类型的赋值是非常直接的。由于主类型容纳了实际的值,而且并非指向一个对象的句柄,所以在为
其赋值的时候,可将来自一个地方的内容复制到另一个地方。例如,假设为主类型使用“A=B”,那么B 处的
内容就复制到A。若接着又修改了A,那么B 根本不会受这种修改的影响。作为一名程序员,这应成为自己的
常识。
但在为对象“赋值”的时候,情况却发生了变化。对一个对象进行操作时,我们真正操作的是它的句柄。所
以倘若“从一个对象到另一个对象”赋值,实际就是将句柄从一个地方复制到另一个地方。这意味着假若为
对象使用“C=D”,那么C 和D 最终都会指向最初只有D 才指向的那个对象。下面这个例子将向大家阐示这一
点。

   举个例子

  

package c03;
class Number {
int i;
}
public class Assignment {
public static void main(String[] args) {
Number n1 = new Number();
Number n2 = new Number();
n1.i = 9;
n2.i = 47;
System.out.println("1: n1.i: " + n1.i +
", n2.i: " + n2.i);
n1 = n2;
System.out.println("2: n1.i: " + n1.i +
", n2.i: " + n2.i);
n1.i = 27;
System.out.println("3: n1.i: " + n1.i +
", n2.i: " + n2.i);
}
} ///:~

 

Number 类非常简单,它的两个实例(n1 和n2)是在main()里创建的。每个Number 中的i 值都赋予了一个不
同的值。随后,将n2 赋给n1,而且n1 发生改变。在许多程序设计语言中,我们都希望n1 和n2 任何时候都
相互独立。但由于我们已赋予了一个句柄,所以下面才是真实的输出:
1: n1.i: 9, n2.i: 47
2: n1.i: 47, n2.i: 47
3: n1.i: 27, n2.i: 27

 

 

详解:看来改变n1 的同时也改变了n2!这是由于无论n1 还是n2 都包含了相同的句柄,它指向相同的对象(最初
的句柄位于n1 内部,指向容纳了值9 的一个对象。在赋值过程中,那个句柄实际已经丢失;它的对象会由
“垃圾收集器”自动清除)。

 

4、

class Letter {
char c;
}
public class PassObject {
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);
f(x);
System.out.println("2: x.c: " + x.c);
}
} ///:~

 

在许多程序设计语言中,f()方法表面上似乎要在方法的作用域内制作自己的自变量Letter y 的一个副本。
但同样地,实际传递的是一个句柄。所以下面这个程序行:
y.c = 'z';
实际改变的是f()之外的对象。输出结果如下:
1: x.c: a
2: x.c: z

 

 

5、i++ 和++i

 

public class AutoInc {
public static void main(String[] args) {
int i = 1;
prt("i : " + i);
prt("++i : " + ++i); // Pre-increment
prt("i++ : " + i++); // Post-increment
prt("i : " + i);
prt("--i : " + --i); // Pre-decrement
prt("i-- : " + i--); // Post-decrement
prt("i : " + i);
}
static void prt(String s) {
System.out.println(s);
}
} ///:~

 

该程序的输出如下:
i : 1
++i : 2
i++ : 2
i : 3
--i : 2
i-- : 2
i : 1

 

详解:从中可以看到,对于前缀形式,我们在执行完运算后才得到值。但对于后缀形式,则是在运算执行之前就得
到值。它们是唯一具有“副作用”的运算符(除那些涉及赋值的以外)。也就是说,它们会改变运算对象,
而不仅仅是使用自己的值。

 

6、关系运算符

    关系运算符生成的是一个“布尔”(Boolean)结果。它们评价的是运算对象值之间的关系。若关系是真实
的,关系表达式会生成true(真);若关系不真实,则生成false(假)。关系运算符包括小于(<)、大于
(>)、小于或等于(<=)、大于或等于(>=)、等于(==)以及不等于(!=)。等于和不等于适用于所有内
建的数据类型,但其他比较不适用于boolean 类型。

public class Equivalence {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1 == n2);
System.out.println(n1 != n2);
}
} ///:~

 

其中,表达式System.out.println(n1 == n2)可打印出内部的布尔比较结果。一般人都会认为输出结果肯定
先是true,再是false,因为两个Integer 对象都是相同的。但尽管对象的内容相同,句柄却是不同的,而
==和!=比较的正好就是对象句柄。所以输出结果实际上先是false,再是true。这自然会使第一次接触的人
感到惊奇。
若想对比两个对象的实际内容是否相同,又该如何操作呢?此时,必须使用所有对象都适用的特殊方法
equals()。但这个方法不适用于“主类型”,那些类型直接使用==和!=即可。下面举例说明如何使用:

 

public class EqualsMethod {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1.equals(n2));
}
} ///:~

 

正如我们预计的那样,此时得到的结果是true。但事情并未到此结束!假设您创建了自己的类,就象下面这

 

class Value {
int i;
}
public class EqualsMethod2 {
public static void main(String[] args) {
Value v1 = new Value();
Value v2 = new Value();
v1.i = v2.i = 100;
66
System.out.println(v1.equals(v2));
}
} ///:~

 

此时的结果又变回了false!这是由于equals()的默认行为是比较句柄。所以除非在自己的新类中改变了
equals(),否则不可能表现出我们希望的行为。不幸的是,要到第7 章才会学习如何改变行为。但要注意
equals()的这种行为方式同时或许能够避免一些“灾难”性的事件。
大多数Java 类库都实现了equals(),所以它实际比较的是对象的内容,而非它们的句柄。 

7、1. 短路
操作逻辑运算符时,我们会遇到一种名为“短路”的情况。这意味着只有明确得出整个表达式真或假的结
论,才会对表达式进行逻辑求值。因此,一个逻辑表达式的所有部分都有可能不进行求值:

public class ShortCircuit {
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) {
if(test1(0) && test2(2) && test3(2))
System.out.println("expression is true");
else
System.out.println("expression is false");
}
} ///:~

 

 

每次测试都会比较自变量,并返回真或假。它不会显示与准备调用什么有关的资料。测试在下面这个表达式
中进行:
if(test1(0)) && test2(2) && test3(2))
68
很自然地,你也许认为所有这三个测试都会得以执行。但希望输出结果不至于使你大吃一惊:
test1(0)
result: true
test2(2)
result: false
expression is false
第一个测试生成一个true 结果,所以表达式求值会继续下去。然而,第二个测试产生了一个false 结果。由
于这意味着整个表达式肯定为false,所以为什么还要继续剩余的表达式呢?这样做只会徒劳无益

 

 

8、三元i f- e l s e 运算符

   布尔表达式 ? 值0:值1

   当然,也可以换用普通的if-else 语句(在后面介绍),但三元运算符更加简洁。还是要先多作一些思量——它很容易就会产生可读性极差的代码

 

 

9、字串运算符+

    这个运算符在Java 里有一项特殊用途:连接不同的字串。

    int x = 0, y = 1, z = 2;
String sString = "x, y, z ";
System.out.println(sString + x + y + z);
在这里,Java 编译程序会将x,y 和z 转换成它们的字串形式,而不是先把它们加到一起。然而,如果使用
下述语句:
System.out.println(x + sString);
那么早期版本的Java 就会提示出错

 

10、类型转换

  “缩小转换”(Narrowing Conversion)的操作(也就是说,脚本是能容纳更多信息的数据类型,将其转换
成容量较小的类型),此时就可能面临信息丢失的危险。此时,编译器会强迫我们进行造型,就好象说:
“这可能是一件危险的事情——如果您想让我不顾一切地做,那么对不起,请明确造型。”而对于“放大转
换”(Widening conversion),则不必进行明确造型,因为新类型肯定能容纳原来类型的信息,不会造成任
何信息的丢失。
Java 允许我们将任何主类型“造型”为其他任何一种主类型,但布尔值(bollean)要除外,后者根本不允
许进行任何造型处理。“类”不允许进行造型。为了将一种类转换成另一种,必须采用特殊的方法(字串是
一种特殊的情况,

 

 11、开关语句

   “开关”(Switch)有时也被划分为一种“选择语句”。根据一个整数表达式的值,switch 语句可从一系列
代码选出一段执行。它的格式如下:
switch(整数选择因子) {
case 整数值1 : 语句; break;
case 整数值2 : 语句; break;
case 整数值3 : 语句; break;
case 整数值4 : 语句; break;
case 整数值5 : 语句; break;
//..
default:语句;

 

。但它要求使用一个选择
因子,并且必须是int 或char 那样的整数值。例如,假若将一个字串或者浮点数作为选择因子使用,那么它
们在switch 语句里是不会工作的。对于非整数类型,则必须使用一系列if 语句。

 

char c = (char)(Math.random() * 26 + 'a');
Math.random()会产生一个double 值,所以26 会转换成double 类型,以便执行乘法运算。这个运算也会产
生一个double 值。这意味着为了执行加法,必须无将'a'转换成一个double。利用一个“造型”,double 结
果会转换回char

将一个float 或double 值造型成整数值后,总是将小数部分“砍掉”,不作任何进位处理。

Math.random();产生值的范围,用数字语言表达,输出值范围是[0,1)

 

12、构造器

 例如,假设我们想创建一个类,令其用标准方式进
行初始化,另外从文件里读取信息来初始化。此时,我们需要两个构建器,一个没有自变量(默认构建
器),另一个将字串作为自变量——用于初始化对象的那个文件的名字。由于都是构建器,所以它们必须有
相同的名字,亦即类名。所以为了让相同的方法名伴随不同的自变量类型使用,“方法过载”是非常关键的
一项措施。同时,尽管方法过载是构建器必需的,但它亦可应用于其他任何方法,且用法非常方便。
在下面这个例子里,我们向大家同时展示了过载构建器和过载的原始方法:

 

import java.util.*;
class Tree {
97
int height;
Tree() {
prt("Planting a seedling");
height = 0;
}
Tree(int i) {
prt("Creating new Tree that is "
+ i + " feet tall");
height = i;
}
void info() {
prt("Tree is " + height
+ " feet tall");
}
void info(String s) {
prt(s + ": Tree is "
+ height + " feet tall");
}
static void prt(String s) {
System.out.println(s);
}
}
public class Overloading {
public static void main(String[] args) {
for(int i = 0; i < 5; i++) {
Tree t = new Tree(i);
t.info();
t.info("overloaded method");
}
// Overloaded constructor:
new Tree();
}
} ///:~

 区分过载方法

若稍微思考几秒钟,就会想到这样一个问题:除根据自变量的类型,程序员如何区分两个同名方法的差异
呢?
即使自变量的顺序也足够我们区分两个方法(尽管我们通常不愿意采用这种方法,因为它会产生难以维护的

 

主类型的过载

若观察这个程序的输出,就会发现常数值5 被当作一个int 值处理。所以假若可以使用一个过载的方法,就
能获取它使用的int 值。在其他所有情况下,若我们的数据类型“小于”方法中使用的自变量,就会对那种
数据类型进行“转型”处理。char 获得的效果稍有些不同,这是由于假期它没有发现一个准确的char 匹
配,就会转型为int

大家可注意到这是一种“缩小转换”。也就是说,在造型或转型过程中可能丢失一些信息(显示转换);

分享到:
评论

相关推荐

    Think In Java 学习笔记

    《Think In Java 学习笔记》 在Java编程语言中,对象是核心概念,它们代表着程序中的实体和行为。对象具有状态、行为和标识,是现实世界概念在软件中的映射。对象的状态由其内部的属性(或称数据成员)表示,行为则...

    Think in java学习笔记

    ### Think in Java 学习笔记知识点总结 #### 第1章:对象导论 - **一切皆为对象**:Java 中的几乎所有事物都是对象,对象通过发送消息的方式进行交互。 - **对象模型**:每个对象都有自己的存储空间,该空间由其他...

    Think in java读书笔记

    《Think in Java》读书笔记深度解析 一、构建器(Constructor) 构建器是Java中用于初始化新对象的一种特殊方法,其显著特征是没有返回值类型,包括`void`。这一点与普通方法形成鲜明对比,普通方法即便没有实际...

    王者归来之Thinking in java读书笔记

    《王者归来之Thinking in Java读书笔记》是对Bruce Eckel的经典之作《Thinking in Java》第四版的深度学习与总结。这本书是Java程序员的必备参考书,它深入浅出地阐述了Java语言的核心概念和技术,旨在帮助读者理解...

    java1think in java笔记(111)---打印 (2008-04-24 16:58:28)

    在给定的代码中,我们看到一个名为`PrintDemo`的类,这个类展示了如何在Java中使用`Graphics`对象进行文本和图形的打印。以下是该代码涉及到的主要知识点: 1. **默认的打印行为**: - `print()`方法通常会调用`...

    原版think in java4

    《原版Think in Java 4》是一本深受程序员喜爱的经典Java编程教材,由 Bruce Eckel 撰写。这本书以其深入浅出的讲解方式和全面的内容覆盖,成为了学习Java语言的重要参考书籍。中文版的出现使得更多的中国读者能够无...

    thinkinjava源码-learn-think-in-java:学习ThinkinJava的笔记和

    这个压缩包"thinkinjava源码-learn-think-in-java"包含了作者在阅读和学习《Think in Java》时的笔记和代码实现,这为我们提供了宝贵的实践示例和理解书中理论的窗口。 1. **源码分析**:学习源码是提高编程技能的...

    java学习资料/笔记整理(二)

    java学习资料/笔记整理,含java语言入门中文版、JDK1.4.2手册、JSP由浅入深、Think In Java(中英文)、Thinking_in_Java_chinese_version、精通swing程序设计等8个编译的HTML帮助文档....

    从Java菜鸟到专家的资料

    2. **Think In Java.chm**:这是经典的《深入思考Java》电子版,作者Bruce Eckel深入浅出地讲解了Java语言的核心概念,包括面向对象编程、集合框架、多线程、异常处理等内容,是Java初学者必读的书籍之一。...

    java离线文档系列.7z

    最后,"Think In Java.chm"同样源于《Thinking in Java》,这本电子版可能包含完整的书本内容,包括类与对象、泛型、并发、IO流等主题,是提高Java编程思维的重要教材。 总的来说,这个压缩包为Java初学者和进阶者...

    恒生电子JAVA笔试试题-Thinking-In-Java-Notes:ThinkinginJava学习笔记

    Java》学习笔记 [TOC] 阅读计划 章节列表 对象导论 一切都是对象 操作符 控制执行流程 初始化与清理 访问权限控制 复用类 多态 接口 内部类 持有对象 通过异常处理错误 字符串 类型信息 泛型 数组 容器深入研究 Java...

    各类chm文件大集合,以及java web学习比较

    css参考手册.chm ...JavaScript参考手册中文版.chm java包(简例)中文版.chm ...Think In Java.chm Browser对象笔记.doc DHTML笔记.doc HTML DOM笔记.doc javascript笔记.doc JQuery笔记.doc XML笔记.doc

    thinkInJava:编程思想源码及答案笔记

    《编程思想源码及答案笔记》是一份深入探讨Java编程技术的宝贵资源,它基于《Think in Java》这本书,该书由Bruce Eckel撰写,是许多程序员学习Java的首选教材。这份笔记结合了书中的理论知识与实际源码,旨在帮助...

    Notes:This is a learning note | Java基础,JVM,源码,大数据,面经

    Think In Java Java容器 Java并发 Java Concurrency in Practice 对象的共享 对象的组合 基础构建模块 JavaGC监控与优化 垃圾回收机制 垃圾回收机制的监控 优化垃圾回收机制 Apache的MaxClients参数详解及其在Tomcat...

    求职英语 面试英语 英语 求职

    例如,你可能需要准备如何用英语表达自己的专业技能(如:“I specialize in software development and have hands-on experience with Java and Python.”)、工作成就(如:“During my previous role, I led a ...

Global site tag (gtag.js) - Google Analytics