《Core Java Volume 1》(Java核心技术卷1)读书笔记、代码。
Ch03
不可变字符串有一个优点:编译器可以让字符串共享。 共享(如复制字符串,常用)带来的高效率远远胜过于提取、 拼接字符串(少用)所带来的低效率。
public class Const {
// 类常量
public static final double MY_R = 5.0;
public void oneMethod() {
final double PI = 3.14; // 常量 局部
final double X;
X = 4;
double aaa = MY_R * PI * 2;
double sinX = sin(PI);
}
public void otherMethod() {
System.out.print(MY_R); // 不同方法都可使用
System.out.print(Const.MY_R); // 其他类可使用(当MY_R为public)
}
public static void stringTest() {
List<String> strings = new LinkedList<>();
strings.add("连字符");
strings.add("拼接");
strings.add("字符串");
System.out.println(String.join("-", strings));
String text = "SubstringOfString";
String copyText = text;
if (text == copyText) {
System.out.println("不可变字符串字符共享(内存仅一份)");
}
String subS = text.substring(0, 9); // len = end - start = 9 - 0
if (subS.equals("Substring") && "Substring".equals(subS)) { // 也可以是字符串字面量"Substring"
System.out.println("内容相同");
}
if (!(subS == "Substring")) {
System.out.println("但存放位置不同(只有字符串常量是共享的,而+ 或 substring 等操作产生的结果并不是共享的)");
}
if (text != null && text.length() != 0) { // 先检查null
System.out.println("text为null:无对象与text变量关联;text为空串:长度=0内容=“”的对象");
}
}
static void test(int[] a) {
a[0] = 11;
}
public static void arrayTest() {
int[] a = {1, 2, 3};
a = new int[2]; // [0, 0]
a = new int[] {4, 5, 6, 7}; // 匿名数组
int[] refA = a; // 引用同一个数组
refA[0] = 3; // a[0] == 3
int[] copyA = Arrays.copyOf(a, a.length); // 拷贝到新数组
copyA[0] = 2; // a[0] == 3
test(a);
Arrays.sort(a);
System.out.println(Arrays.toString(a));
int[][] b23 = new int[2][3];
int[][] c2x = new int[2][]; // 可创建不规则数组
c2x[0] = a;
c2x[1] = new int[10];
System.out.println(Arrays.deepToString(c2x));
}
public static void main(String[] args) {
arrayTest();
}
}
Ch04 对象和类
OOP过程:
- 设计类(识别类->名词)
- 每个类中添加方法(方法->动词)
在 Java 中, package 与 import(非必须,只为简便) 语句类似于 C++ 中的 namespace 和 using 指令,而不是 include(必须)。
声明为 protected:如果需要限制某个方法的使用。这表明子类(可能很熟悉祖先类)得到信任,可以正确地使用这个方法,而其他类则不行。
public class Example {
private int id;
public Example(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id){
this.id = id;
}
@Override
public String toString() {
return "Example{" +
"id=" + id +
'}';
}
}
import static java.lang.System.out;
public class Class {
private final String name; // final域
public Class(String name) {
this.name = name; // 若设置后不再被修改(无setName),使用final
}
public String getName() {
return this.name;
}
private static int nextId = 1; // 所谓静态域即类域:属于类,不属于对象,0或多个对象共享一个nextId域。(当该域与对象无关时使用)
public static final double PI = 3.14; // public的静态常量(如Math.PI)
public static void staticMethod() {
nextId++;
// System.out.println(name); 静态方法不能访问name实例域,因为它不能操作对象
}
public void nonStaticMethod() {
nextId++;
System.out.println(name);
}
/**
* 参数传递,一般有按值value传递、按引用reference传递,Java只有按值传递
* Java有基本数据类型(数值、布尔)和引用类型(数组、类、接口)
* 方法的参数为基本类型时不能修改原数据,如swap(int a, int b)无效
* 方法的参数为引用类型时可以修改原数据的状态,如setId(Example e)、sort(int[] a)
* 但不能使原数据引用新数据,swap(Example a, Example b)无效
*/
public static void swap(Example e1, Example e2) {
Example temp = e1;
e1 = e2;
e2 = temp;
System.out.println("In swap() e1 e2 has swapped: " + e1.getId() + " " + e2.getId());
}
public static void Obj() {
System.out.println(LocalDate.now());
List<Example> examples = new ArrayList<>();
Example example = new Example(1);
examples.add(example);
Example example2 = example; // 指向同一内存
example2.setId(2);
examples.add(example2);
// Example example3 = example.clone(); // error
Date date = new Date();
Date date2 = (Date) date.clone(); // 对象拷贝
for (Example e : examples) {
System.out.println(e);
}
Example E1 = new Example(1);
Example E2 = new Example(2);
swap(E1, E2);
System.out.println("but E1 E2 didn't: " + E1.getId() + " " + E2.getId());
}
}
// 初始化数据域的多种途径
public class StaticBlock {
private int id;
private static int nextId;
private String name = "显示声明";
public StaticBlock(){ // 在构造器初始化
id = -1;
nextId = 1;
}
public StaticBlock(int id, String name) {
this.id = id;
this.name = name;
}
public StaticBlock(int id) {
this(id, "this()调用另一个构造器");
}
{ // 先于构造器执行
System.out.print("object init block: " + id);
id = 2;
id *= id;
id += nextId;
System.out.println(" - " + id);
}
static { // 类第一次加载时就执行
System.out.print("static init block: " + nextId);
nextId = 10;
nextId *= nextId;
System.out.println(" - " + nextId);
}
public int getId() {
return id;
}
public static void main(String[] args) {
StaticBlock sb = new StaticBlock(1);
System.out.println(sb.getId());
String s;
out.println("导入静态包,System.out简写");
// static init block: 0 - 100
// object init block: 0 - 104
// 1
}
}
Ch05 继承
抽象类:顶层的类,包含抽象的、通用的域、方法不能被实例化
public abstract class AbsPeople {
private String name;
public AbsPeople(String name) {
this.name = name;
}
// 可以有(甚至全为)非抽象方法
public String getName() {
return name;
}
// 占位作用,待子类各自具体表述
public abstract String getDescription();
}
扩展抽象类两种选择:
- 不实现或实现部分抽象方法,自身也为抽象类
- 实现全部抽象方法
public class Student extends AbsPeople {
private String major;
public Student(String name, String major) {
super(name);
this.major = major;
}
@Override
public String getDescription() {
return "major in " + major;
}
}
类实现接口需要对接口所有方法给出实现。
public class Teacher extends AbsPeople implements Comparable<Teacher>{
private double age;
public Teacher(String name,double age) {
super(name);
this.age = age;
}
@Override
public String getDescription() {
return "age of " + age;
}
@Override
public int compareTo(Teacher o) {
// 对于浮点数,防四舍五入,必须使用Double.compare()
return Double.compare(age, o.age); // o.age可以访问
}
}
使用继承原则:is-a
关系,如经理是雇员
public class Inherit {
// 多态:一个对象变量(超类)可以指示多种实际类型(超类、子类、孙类)
public static void Poly() {
Example[] examples = new Example[2];
examples[0] = new Example(1);
examples[1] = new PeopleExample(2);
out.println("e[0]: " + examples[0].getId());
out.println("e[1]: " + examples[1].getId());
// out.println(examples[1].getName()); // Error,声明类型为超类,不能调用子类方法
PeopleExample people = new PeopleExample(3);
out.println(people.getName() + people.getId());
if (examples[1] instanceof PeopleExample) {
people = (PeopleExample) examples[1]; // 超类转型成子类
}
out.println(people.getName() + people.getId()); // 现在可以使用getName()
}
// 抽象类
public static void AbsClass() {
AbsPeople people[] = new AbsPeople[2];
people[0] = new Student("eyedeng", "CS");
people[1] = new Teacher("hoping", 35);
for (AbsPeople p :
people) {
out.print(p.getName() + ": ");
out.println(p.getDescription()); // 可由超类变量调用,声明了抽象方法的好处
}
}
}
class PeopleExample extends Example {
private String name;
public PeopleExample(int id) {
super(id);
name = "Me";
}
@Override
public int getId() {
return super.getId() + 10;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
final class LastExample extends PeopleExample {
String description = "final:不能被继承";
public LastExample(int id) {
super(id);
}
}
Ch06 接口 lambda 内部类
接口中的所有方法自动地属于public。因此,声明方法时不必提供关键字 public。接口绝不能含有实例域 。
接口中的域将被自动设为 public static final(常量)。
public interface IntePeople {
String LOCATION = "China"; // 常量 不必要声明为public static final(自动声明了)
String getDescription(); // 不必要声明为public
}
服务提供商:如果类遵从某个特定接口,那么就履行这项服务。
package com.company.coreJava.v1ch06;
import com.company.coreJava.v1ch05.Teacher;
public class Interface {
// 服务提供商:如果类遵从某个特定接口,那么就履行这项服务
// Arrays.sort()服务:对象所属的类必须实现了Comparable接口
public static void Sort() {
Teacher t[] = new Teacher[4];
t[0] = new Teacher("t1", 35.10001);
t[1] = new Teacher("t2", 35.1001);
t[2] = new Teacher("t3", 35.000000401);
t[3] = new Teacher("t4", 35.0000004);
Arrays.sort(t);
for (Teacher te :
t) {
System.out.println(te.getName() + te.getDescription());
}
}
}
default方法:为接口方法提供一个默认实现,这样实现此接口时只需实现部分我所关心的方法。
接口演化
(接口新增方法)时,如果是非默认方法,则不能保证源代码兼容(实现者都要跟着加)。
解决default方法冲突:
-
超类优先(当
interface Child extends Father
)。 -
覆盖此方法解决接口二义性(当
class C implements I1, I2
)。 -
类优先原则。
lambda表达式来由:需要将一个代码块传递到某个对象(一个定时器, 或者一个 sort 方法)。这个代码块会在将来某个时间调用。在 Java 中传递一个代码段并不容易,必须构造一个对象,这个对象的类需要有一个方法能包含所需的代码(接口常用来封装代码,如ActionListener)。对于只有一个抽象方法的接口(称为函数式接口), 需要这种接口的对象时, 就可以提供一个 lambda 表达 式。最好把 lambda 表达式看作是一个函数(类比JS的箭头表达式函数),而不是一个对象,另外要接受 lambda 表达式可以传递到函数式接口。lambda表达式所能做的也就是转换为函数式接口。
多年来,Java 程序员习惯的做法是用匿名内部类实现事件监听器和其他回调。如今最好还是使用 lambda 表达式。
public class TimerTest
{
public static void main(String[] args)
{
// 原理
ActionListener listener1 = new TimePrinter();
// 过往做法:匿名内部类
ActionListener listener2 = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone, the time is " + new Date());
Toolkit.getDefaultToolkit().beep();
}
};
ActionListener listener3 = e -> {
System.out.println("At the tone, the time is " + new Date());
Toolkit.getDefaultToolkit().beep();
};
// 最佳,直接传入一个lambda表达式
Timer t = new Timer(2000, listener3);
t.start();
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
class TimePrinter implements ActionListener
{
@Override
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is " + new Date());
Toolkit.getDefaultToolkit().beep();
}
}