Java编程思想
Java 编程思想为 Java 开发的圭臬, 是 Java 开发的经典手册. 作为一个开发人员还是建议多看一看. 从大学时起到现在已经开发多年, 也看过多遍, 随着年龄的增长和开发经验的增加, 每次重新阅读侯都会有新的理解, 所谓温故而知新. 但也存在问题, 一个是阅读时获得新的理解隔一段事件后容易遗忘. 二是每次阅读后做的纸质笔记容易丢失和难以拓展. 遂决定以电子版记之~~
Java编程思想基于 jdk 1.5版本,
描述: 尽管 Java 是基于 C++ 的, 但相比之下, Java 是一种更“纯粹”的面向对象程序设计语言
对象的存储
特例: 基本类型存放在堆栈中
char c = 'x';
Character ch = new Character('x');
自动包装:编译器可以自动将基本类型转化为包装器类型
Character ch = 'x';
自动拆包: 编译器自动将包装器类型转换为基本类型
char c = ch;
在最底层, Java 中的数据是通过使用操作符来操作的
就像有知觉的生物一样, 程序必须在执行过程中控制它的世界, 并作出选择. 在 Java 中, 你要使用执行控制语句来做出选择
无穷循环的两种基本方式: for(;😉 和 while(true)
goto 是 Java 中的一个保留字, 目前的版本中没有使用它. goto 的替代机制 – 标签, 用于完成循环中需要跳转的功能. 用于循环语句之前, 配合 break 和 continue 语句一起使用, 中断循环到直接到标签所在的地方
标签是后面跟有冒号的标识符
// eg
label1:
for(int i=0; i<10; i++){
label2:
for(int j=0; j<10; j++){
if(j=2){
continue label1; // 中断内层循环, 外层开始下一次循环
} else {
break label1; // 跳转并退出外层循环
}
}
}
随着计算机革命的发展, “不安全” 的编程方式已逐渐成为编程代价高昂的主因之一
void f(long a){System.print.out("long a")};
f(5); // 5为 int 类型, 编译不会报错
如果参数高于方法能接受的基本数据类型, 则需要强制类型转换
void f(char a){System.print.out("char a")};
f((char)8); // 5为 int 类型, 编译不会报错
访问控制 (或隐藏具体实现) 与 “最初的实现并不恰当” 有关
四种访问权限控制: public、protected、包访问权限(没有关键字) 和 private
复用代码时 Java 众多引人注目的功能址以. 但要想成为极具革命性的语言, 仅仅能够复制代码并对之加以改变时不够的, 它还必须能够做更多的事情
当创建一个导出类的对象时, 该类包含了一个基类的子对象. 这个子对象和你用基类直接创建的对象时一样的. 二者的却别在于, 后者来源于外部, 而基类的子对象包含在导出类的内部(导出类初始化过程: 编译器先调用基类的构造器初始化基类, 然后再初始化子类, 所以子类可以调用父类的方法)
带参宿的构造器: 如果想调用带参数的构造器, 可以使用super来调用, 所以 super 要放在构造器的第一行调用
class Game {
Game(int i){
System.out.print("Game constructor")
}
}
class BoardGame extends Game{
BoardGame(int i){
super(i);
System.out.print("BoardGame constructor");
}
}
“我曾经被问到’求教, Babbage 先生, 如果你向机器中输入错误的数字, 可以得到正确的答案吗?’ 我无法恰当的理解产生这种问题的概念上的混淆”
在面向对象设计语言中, 多态是继数据抽象和继承之后的第三种基本特征
后期绑定: 也叫动态绑定或运行时绑定
Shape s = new Circle();
s.draw(); // 实际调用的是导出类 Circle 的 draw() 方法
接口和内部类为我们提供了一种将接口和实现分离的更加结构化的方法
可以将一个类的定义放在另一个类的定义的内部, 这就是内部类
public class Outer{
public class Inner{
}
}
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
如果一个程序只包含固定数量的且其生命周期都是已知的对象, 那么这是一个非常简单的程序.
程序需要在任意时刻和任意位置创建任意数量的对象, 因此就不能依靠命名的引用来持有每一个对象.
因为你不知道实际上需要需要多少这样的引用.
Java 实用类库还提供了一套相当完整的容器类来解决这个问题, 并且容器类都可以自动调整自己的尺寸.
你应该创建一个具体的对象, 将其转型为对应的接口, 然后再其余的代码中都使用这个接口.
这样做的目的是, 需要修改实现的时候, 只需要再创建处修改就可以了, eg:
List<Apple> apples = new ArrayList<>();
apples = new LinkedList<>();
编码中只是使用容器, 不着调或者不关心容器的类型, 那么如何才能不重写代码就可以应用于不同类型的容器?
迭代器(也是一种设计模式)的概念可以用于达成此目的. 迭代器是一个对象, 它的工作是遍历并选择序列中的对象, 而不必知道底层的数据结构.
“栈” 通常值 “后进先出” 容器.
只需要栈的行为(入栈 push, 出栈 pop,返回栈顶元素 peek, 判空 empty)的话, jdk 原始的 Stack 通过继承 LinkedList 实现, 并不是太合适, 可以单独定义一个 Stack,或者引入 import net.mindview.util.Stack 类
import java.util.LinkedList;
public class Stack<T> {
private LinkedList<T> storage = new LinkedList<>();
// 入栈
public void push(T v){storage.addFirst(v);}
// 返回栈顶元素
public T peek(){return storage.getFirst();}
// 出栈
public T pop(){return storage.removeFirst();}
public boolean empty(){return storage.isEmpty();}
public String toString(){return storage.toString();}
}
Java 的基本理念是 “结构不佳的代码不能运行”
发现异常最理想的时机是在编译阶段, 也就是在你试图运行之前. 然而, 编译期间并不能找出所有的错误, 余下的问题必须在运行期间解决. 这就需要错误源能通过某种方式, 把适当的信息传递给某个接受者 – 该接收者将知道如何正确处理这个问题
可以证明, 字符串操作是计算机程序设计中最常见的行为
// s 是局部变量, 该方法执行的时候才存在, 方法执行完后消失
public String upcase(String s){
// 返回一个新的引用给调用者
return s.toUpperCase();
}
public static void main(String[] args){
String q = "aa";
print(q);
String qq = upcase(q);
print(qq);
// 原对象值未被改变
print(q);
}
// output
// aa
// AA
// aa
一个操作符在应用于特定的类时, 被赋予了特殊的意义(用于 String 的 “+” 与 “+=” 是 Java 中仅有的两个重载过得操作符)
// "+" 被重载过, 作用与于 String 类时, 自动引入 StringBuilder 对象, 调用三次 append() 方法, 最终调用 toString() 方法生成结果, 并存为 s. 因为 StringBuilder 更加高效. 所以只产生一个对象
String s = "abc" + "def" + "ghi";