1 ~ 9 章

  • Java 除了基本类型之外,操纵其他元素都是对象。对对象的操作都是通过引用,两者的关系如同电视机与遥控器。引用是可以脱离对象单独存在的,但是这时遥控器并不起作用。
  • 引用保存在堆栈中,对象保存在堆中,基本数据类型保存在堆栈中。
  • 在作用域内定义的基本变量只可用于作用域前。
{
    int x = 12;
}

int y = x; //illegal
  • 在作用域内创建的对象,在作用域外引用消失了,但是创建的对象仍然存在于内存中,垃圾回收器会在合适的时间对没有任何引用的对象进行清理。
{
    String s = new String("a string");
}
  • 对于类中的字段,基本类型不能在对象间共享。
class DataOnly {
    int a;
    double b;
    boolean c;
}

DataOnly obj1 = new DataOnly();
DataOnly obj2 = new DataOnly();
obj1.a = 1;  //obj1.a == 1
obj2.a = obj1.a;  //obj2.a == 1
obj2.a = 2;  //obj2.a == 2 , obj1.a == 1
  • Java 的包命名采用Internet域名的反转,目的是有效的避免类的命名冲突。
  • Java.lang 是所有Java文件默认导入的类库。
  • == 比较引用,equals 对于大多数 java 类库比较数值,但是自己创建的类需要重写 equals,否则默认比较引用。
  • 虽然 goto 滥用会造成很大的危害,但是当有多层循环嵌套存在时,标签 与 continue 、break 结合可以大大简化编程。
label1:
outer-iteration {
    inter-interation {
        break;
        continue;
        cntinue label1;
        break label1;
    }
}
  • switch 要求的选择引子必须是 int 或者 char 那样的整数值,可以使用 enum。
  • 显示使用 this 时表示调用该方法的对象,需要传递调用该方法的对象时使用。
  • finalize 方法:
    • 针对的是采用特殊方式为对象分配存储空间(例如使用本地方法),对存储空间进行释放。
    • 希望进行除释放存储空间外的清理工作,因为垃圾回收器唯一的作用就是释放存储空间,并不能做其他事情。
    • 另一个有趣的用法是用来发现一些隐晦的 bug,比如对象在销毁前必须执行一些操作而如果程序员忘记做这些事情,finalize 方法碰巧执行的时候就会发现。
  • 局部变量强制要求赋初值,但是类成员变量会自动赋初值。
  • 数组创建:
    • String[] a = new String[3] ; 创建的是一个引用数组,初始化并没有完成;
    • String[] a = { “a” , “b” , “c” } ; 创建数组只能用在数组定义时;
    • String[] a = new String[]{ “a” , “b” , “c” } ; 可以用在参数传递等任何地方;
    • Object … args ; 使用可变参数列表可以简化编写数组语法,此时编译器会自动填充成数组,仍然可以使用 foreach 语法。
  • java package 的主要意义在于使得 java 文件的组织结构具有命名空间。
    • 创建独一无二的包名:通过使用域名的反转来实现;
    • 包名需要与文件名和结构相符,编译器在查找类文件时,会在 CLASSPATH 所指定的目录中查找。jar 文件也需要包含在 CLASSPATH 中。
  • java 的权限控制解决了一个重要的问题 “ 将变动的事物与保持不变的事物区别开来 ”,类库的开发者可以方便的修改类库。
    • 默认包权限:包内可以自由访问;
    • public:所有类都可以使用;
    • private:除了包含该成员的类外都不可以访问;
    • protected:包访问权限及继承。
  • 复用可以通过组合和继承来实现:
    • 组合通常用于在新类中使用现有类的功能而非接口,即在新类中中嵌入某个对象以实现某些功能,单新类的用户看到的只是为新类所定义的接口而非所嵌入对象的接口。所以通常嵌入的是 private 对象。
    • 通常需要选择组合而不是继承,is-a 用继承来表示,has-a 用组合来表示。如果必须向上转型则继承是必须的。
    • 组合的一种特殊情况是代理模式,将嵌入对象的接口重新暴露出来而不是隐藏:
  • 组合中的引用初始化位置:
    • 定义时初始化
    • 构造器中初始化
    • 惰性初始化
    • 子类对象创建时,其实包含了一个基类的对象,对于基类的初始化,默认构造器会自动调用,而带参数的构造器必须在子类的构造器中显示调用 super,且必须放在最前面。
    • 确保正确清理:如果需要类清理一些东西,必须显示的编写一个方法来让客户端程序员或者基类知道。清理方法的调用和 C++ 的析构函数的顺序思路是一样的。
    • 重载和覆写:覆盖是指子类与父类的方法返回值、参数及名称完全相同,主要用在向上转型上。而重载是子类或者父类两个方法的参数不同而名称相同的情况。使用 @override 可以避免覆写时出错编程重载。
    • final 修饰数据、方法及类的情况比较复杂:
    • 类加载和初始化过程:
    • 通过向上转型,多态消除了类型之间的耦合关系,子类和父类 is-a 的关系使得可以安全的用子类替换父类。
    • final 方法和 static 方法不支持后期绑定(动态绑定),他们是前期绑定的。虽然这样可以提高运行效率,但是最好根据设计来决定是否需要 final。
    • 子类不能覆盖父类的私有方法,此时在父类中使用向上转型并调用该方法执行的是父类的私有方法。
class Super {
    private void f() {
        System.out.println("super f()");
    }
    public static void main(String[] args) {
        Super obj = new Sub();
        obj.f();
    }
}

class Sub extends Super {
    public void f() {
        System.out.println("sub f()");
    }
}
  • 域和静态方法同样不支持覆盖,域访问操作由编译器解析,而静态方法与类绑定而不是单个的对象。
class Super {
    public int field = 0public int getField() { return field; }
}

class Sub extends Super {
    public int field = 1public int getField() { return field; }
    public int getSuperField() { return super.field; }
}

Class Test {
    public static void main(String[] args) {
        Super obj = new Sub();
        System.out.println(obj.field); // 0
        System.out.println(obj.getField()); // 1
        System.out.println(obj.getSuperField()); // 0
    }
}

实际上很少有这种情况,通常会将所有域设置为 private,通常不会对基类中的域和导出类中的域赋予同样的名字。

  • 在构造器中使用具有多态的方法会产生意向不到的结果:
class Super {
    Super() {
        System.out.println("super class , before f()");
        f();   
        System.out.println("super class , after f()");

    }
    public void f() {
        System.out.println("super f()");
    }
}

class Sub extends Super {
    private int field = 1;
    Sub() {
        System.out.println("sub class , field = " + field);
    }
    public void f() {
        System.out.println("sub f() , field = " + field);
    }
    public static void main(String[] args){
        new Sub();
    }
}
/*
output:
super class , before f()
sub f() , field = 0
super class , after f()
sub class , field = 1
*/

初始化时,会首先将分配给对象的存储空间初始化成二进制的零,然后调用基类的构造器。在父类的构造器中调用 f(), 调用了子类覆盖的方法,此时子类还没有被初始化。所以在编写构造器时,尽可能不要调用其他方法,或者调用 private 和 final 方法。覆盖的意义:父类的方法被替代,即使是在父类中调用

  • 接口提供了将接口与实现分离的更加结构化的方法。而抽象类的好处是强制客户端程序员不能生成没有实际意义的基类,因为他仅规定了方法而具体的实现取决于子类的覆盖。如何选择接口和抽象类:
    • 接口和抽象类都可以向上转型并且他们都可以防止客户端程序员创建该类的对象。
    • 接口比抽象类更加灵活,因为接口可以多重继承。
    • 接口不可以包含私有数据,但是抽象类可以。如果要创建不带任何方法定义和成员变量的基类,应该选择接口而不是抽象类。
    • 书中又强调了适当的原则是优先选择类而不是接口。如果接口的必需性变得非常明确,那么就应该进行重构。接口是一种重要的工具,但是他们容易被滥用。
  • 使用接口的继承可以扩展接口,当实现多个接口时,具有覆盖特征的方法都归为同一个方法:
interface I1 { void f(); }
interface I2 { int f(int i); }
interface I3 { int f(); }
class C { public int f() { return 1; } }
class C2 implements I1,I2 {
    public void f() {}
    public int f(int i) { return 1 ; }
}
class C3 extends C implements I2 {
    public int f(int i) { return 1; }
}
class C4 extends C implents I3 {
   //可以不覆盖,基类已经有这个方法
   //public int f() { return 1; } 
}
// 仅返回值不同的方法会报错
//! class C5 extends C implements I1 {}
//! interface I4 extends I1,I3 {}
  • 接口中的域自动都是 public,static,final 的,所以在 Java SE5 以前,这是产生与 C 或者 C++ enum 具有相同效果类型的唯一途径。
public interface Colors {
    int RED = 1, BLACK = 2, GREEN = 3;
}
  • 本章穿插讲了工厂模式,适配器模式,策略模式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值