Java核心技术(卷I)读书笔记 第四~六章

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/broken_promise/article/details/77966979

Java核心技术(卷I)第四~六章

面向对象设计部分 OOP

第四章 对象与类

  1. 类之间的关系有
    uses-a: 依赖,一个类的方法操纵另一个类的对象。
    has-a:聚合,一个类的对象包含另一个类的对象。
    is-a:继承,用于表示特殊和一般关系。
  2. 构造器( constructor ):构造新实例,应和类名相同,在构造器前面加上 new 操作符用于构造对象。
    Date birthday = new Date()
    注意:Date deadline 只是定义了一个对象变量,她可以引用Date类型的对象,但deadline本事不是对象。在Java中,任何对象变量的值都是对存储在另一个地方的一个对象的引用,new 操作符的返回值也是一个引用。可以显示地将对象变量设置为 null 表明这个对象变量目前没有引用任何值。
    另外,局部变量不会自动初始化为null。不能对已经存在的对象调用构造器达到重新设置实例域的目的。不要在构造器中定义与实例域重名的局部变量。
    如果类中提供了至少一个构造器,但是没有提供无参数的构造器,则在构造对象的时候,如果没有提供参数就被视为不合法。
    调用另一个构造器:如果构造器的第一个语句行如this(…),那么这个构造器将调用同类中的另一个构造器。
    public Employee{
    this('Employee #' + nextId, s);
    nextId++;
    }
  3. 更改器( mutator method )和访问器(accessor method)方法:对实例做出修改的叫做更改器方法,仅访问实例域而不进行修改的叫做访问器方法。通常在访问器前面加 get, 在更改器前面加get
  4. 隐式( implicit )参数和显式( explicit )参数:
    public void raiseSalary(double byPercent){
    salary += salary*byPercent/100; }

    第一个参数称为隐式参数,是出现在方法名之前的类对象,关键字 this 表示隐式参数;第二个参数是方法名后面括号中的数值,显式参数。
  5. 在封装时,不要编写返回引用可变对象的访问器方法。如果要返回一个可变对象的引用,应先对其 克隆( clone ), 对象的克隆是指存放在另一个位置上的对象副本。
  6. final实例域:final修饰基本( primitive )类型域,或不可变( immutable )类型域。构建对象时必须在每一个构造器执行之后初始化这样的域。
  7. 静态域和静态方法:如果将域定义为static, 那么每个类中只有一个这样的域,每一个对象对于所有的实例域都有一份自己的拷贝。即使没有对象,静态域也存在,它属于类,而不属于任何对象。
    静态方法是一种不能向对象操作的方法,如Math.pow(x, a),没有隐式参数。因为静态方法不能操作对象,所以不能再静态方法中访问实例域,但可以访问自身类中的静态域。我们建议使用类名来调用静态方法
    使用静态方法的两种情况:
    • 一个方法不需要访问对象状态,其所需参数都是通过显示参数提供。
    • 一个方法只需要访问类的静态域。
  8. 工厂( Factory )方法:
  9. 方法参数:Java总是采用按值调用( call by value )。方法参数共有两种类型:基本数据类型和对象引用。方法得到的是对象引用的拷贝,对象引用及其拷贝同时引用同一个对象。
    Java 中方法参数的使用情况:
    • 一个方法不能修改一个基本数据类型的参数。
    • 一个方法可以改变一个对象的参数。
    • 一个方法不能让对象参数引用一个新的对象。
  10. 显式域初始化:可以在类定义中直接将一个值付给任何域,也可以调方法对域进行初始化。
  11. 初始化块( initialization block ):在一个类的声明中,可以包含多个代码块,只要构造类的对象,这些块就会自动执行,建议将初始化块放在域定义之后。
    静态的初始化块:将代码放在一个块中,并标记关键字static。
  12. 可以为一个类添加 finalize 方法,这个方法将在垃圾回收器清除对象之前调用。但在实际应用中,不要依赖使用这个方法回收任何短缺资源,因为很难知道这个方法什么时候被调用。
  13. 包 (package):使用包的主要原因是确保类名的唯一性。
  14. 类的导入:
    • 第一种方式是在每个类名前添加完整的包名。
      java.util.Date today = new java.util.Date();
    • 使用import语句导入一个特定的类或者整个包。
      import java.util.*; 只能使用星号导入一个包。
      import还增加了导入静态方法和静态域的功能。
      `import static java.lang.System.*;
  15. 要想将一个类放到包中,就要将包的名字放在源文件的开头。
    package com.horstmann.corejava
    如果没有在源文件中放置package中,这个源文件的类就被放置到一个默认的包中( default package ),没有名字。
  16. 文档注释( javadoc ):可以由源文件生成一个HTML文档。在源代码中添加以专用的定界符/* 开始的注释。每个/*…*/文档注释在标记后紧跟自由格式文本( free-form text ),标记由 @ 开始, 自由文本的第一句应该是一个概要性的句子。
    方法注释:
    • @param:变量描述。可以占据多行并用HTML描述,一个方法的所有@param标记必须放在一起。
    • @return:可以占据多行并用HTML描述
    • @ throws:添加一个注释,表示这个方法有可能抛出异常。
      通用注释:
    • @author 姓名:产生一个作者条目
    • @version 文本:产生一个版本条目
    • @since 文本:产生一个始于条目,可以是对引入特性的描述
    • @deprecated 文本:对类、方法或变量添加一个不再使用的注释,文本中给出取代的建议。
    • @see 引用:这个标记将在”see also”部分添加一个超级链接。可用于类或方法中。
      @see com.horstmann.corejava.Employee#raiseSalary(double)
      一定要使用要使用#分割类名与方法名,或类名与变量名。
      如果@see之后有一个 < 字符就要制定一个超链接,可以链接到任何URL。
      @see <a href="www.horstmann.com/corejava.html">The Core Java home page </a>
      如果@see之后有一个双引号(“)字符,文本就会显示在see also 之后。
    • @link:可以在注释的任何位置放置指向其他类或方法的超级链接,以及插入一个专用的标记。
      {@link package class#feature label}
  17. 类注释:必须放在import语句之后,类定义之前。
    域注释:只需要对公有域( 通常指的是静态常量 )进行注释。
  18. 产生包注释:
    • 提供一个以package.html命名的HTML文件,在标记…之间的所有文本都会被抽取出来。
    • 提供一个以package-info.java命名的java文件。

第五章 继承

  1. 使用关键字 extends 表示继承,所有的继承都是共有继承
    class Manager extends Employee{}
  2. 使用关键字 super 指示编译器调用超类方法。
    double baseSalary = super.getSalary();
    super在构造器中使用。使用super调用构造器的语句必须是子类构造器的第一条语句。如果子类没有显式调用超类构造器,则将自动调用超类默认构造器。
    super(n, s, year, month, day);
  3. 一个变量对象可以指示多种实际类型的现象被称作多态( polymorphism ), 在运行司能够自动地选择调用那个方法的现象叫做动态绑定( dynamic binding ).
  4. 继承层次( inheritance hierarchy ):有一个公共超类派生出来的所有两类的集合。在继承层次中,从某个特定的类到其祖先的路径被称为该类的继承链。
  5. “is-a”是一种置换法则,表示程序中出现超类对象的任何地方都可以用子类对象置换
    Employee e;
    e = new Employee();
    e = new Manager();
    这个例子:
    Manager boss = new Manager()
    Employee[] staff = new Employee[3];
    staff[0] = boss;
    在这个例子中,变量staff[0]与boss引用同一个对象,但编译器将staff[0]看做Employee。
  6. 在覆盖方法时,如果子类中定义了一个与超类签名相同的方法,那么子类中的这个方法就覆盖了超类中的相同签名方法,允许子类将覆盖方法的返回类型定义为原返回类型的子类型。此外,子类方法不能低于超类方法的可见性,特别是,如果超类方法时 public,子类方法一定要声明为 public。
    public Employee getBuddy(){...}
    public Manager getBuddy(){...}
  7. 方法表( method table ):虚拟机预先为每个类创建了一个方法表,其中列出了所有方法的签名和实际调用的方法。这样在调用方法时只需查表即可。
  8. 对象的强制类型转换:仅需要用一对圆括号将目标类名括起来,并放置在需要转换的对象引用之前即可。只能在继承层次内进行类型转换。在将超类转换成子类之前,应该使用 instanceof 进行检查。:一般情况下应尽量少使用类型转换和instanceof运算符。
    Manager boss = (Manager) staff[0];
  9. 抽象类:使用 abstract 关键字。
    public abstract String getDescription(); #不需要实现
    包含一个或者多个抽象方法的类本身必须被声明为抽象类。即使类不含有抽象方法,也可以将类声明为抽象类。
    abstract class Person{ public abstract String getDescription(); }
    除了抽象方法外,抽象类还可以包含具体数据和方法。
    抽象类不能被实例化,但可以定义一个抽象类的对象变量,但它只能引用非抽象子类的对象。
  10. protected:对本包和子类可见。
    private:仅对本类可见。
    public:对所有类可见。
    默认:对本包可见,不需要修饰符。
  11. Object:所有类的超类。如果没有明确指出超类,Object类就被当做是这个类的超类。可以私用Object类型变量引用任何类型的对象。
    Object obj = new Employee("Harry Hacker", 35000);
  12. 完美 equals 的建议:
    • 显式参数命名为otherObject,稍后需要将它转换成叫做other的变量。
    • 检测this和other是否引用用一个对象
      if(this == otherObject) return true;
    • 检测otherObject是否为null,是的话返回false
      if(otherObject == null) return false;
    • 比较this和otherObject是否属于同一个类,如果equals的语句在每个子类中有所改变,就使用getClass检测;或者说子类拥有自己的相等概念,则对称性强制采用getClass进行检测。
      if(getClass() != otherObject.getClass()) return false;
      如果所有子类都有统一的语义,使用instanceof检查。或者说如果由超类决定相等的概念,就使用instanceof进行检测。
      if(!(otherObject instanceof ClassName)) return false;
    • 将otherObject转换为相应的类类型变量。
      ClassName other = (ClassName)otherObject;
    • 现在开始对所有需要比较的域进行比较,使用==比较基本数据类型,使用equals比较对象域。
      return field1 == other.field1 && Object.equals(field2, other.field2);
  13. Object.hashCode 方法:散列码是由对象导出的一个整数值(可以使负数)。在Java 7中,最好使用null安全的Object.hashCode,如果参数为null,这个方法返回0。该方法还可以提供多个参数,对各个参数调用Object.hashCode,并组合这些散列值。
    return Object.hashCode(name, salary, hireDay);
  14. o.toString方法:绝大多数的toString方法都遵循这样的格式:类的名字,随后是一对方括号括起来的值域。
  15. 泛型数组列表ArrayList这个采用参数类型( type parameter )的泛型类( generic class )。
    ArrayList<Employee> staff = new ArrayList<>();
    ArrayList<Integer> a = new ArrayList<>();!!!#这里要使用基本数据类型的包装器,Integer,而不是int
    在Java 7 中可以省去菱形语法右边尖括号里的参数。
    • 使用, add 方法将语速添加到数组列表中。
      staff.add(new Employee("Tony Tester", 30000));
    • 使用, ensureCapacity方法在填充数组前分配容量。
      staff.ensureCapacity(4);
    • 使用, size()方法返回数组列表中包含的实际元素数目。
    • 使用, trimToSize()方法将存储区域的大小调整到当前元素所需要的存储空间数目。
    • 使用, set(int index, E element)方法设置第i个元素。
    • 使用, get(int index)方法返回此列表中指定位置上的元素。
    • 使用, remove(int index):移除此列表中首次出现的指定元素(如果存在)。
      将原始的ArrayList赋值给类型化的ArrayList会得到一个警告。使用类型转换并不能避免出现警告。
  16. 对象包装器和自动装箱:所有的基本烈性都有一个与之对应的类,这些类称为 包装器(wrapper),当Integer类型的数组列表获得或添加值的时候,使用list.add(Integer.valueOf(3));这称为自动装箱(autoboxing)。==运算符也可以应用于对象包装器对象,只不过检测的是对象是否指向同一个存储区域。
  17. 参数数量可变的方法:
    public PrintStream printf(String fmt, Object...args){}
    这里…表示这个方法可以接受任意数量的对象。实际上,printf方法接受两个参数,一个是格式字符串,另一个是Object[]数组,其中保存着所有参数(如果提供的是整形数组或者其他基本类型的值,自动装箱功能将把它们转换成对象。)
  18. 枚举类:所有没枚举类都是Enume类的子类。
    public enum Size{
    SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
    private String abbreviation;
    }
    • toString():返回枚举类的常量名。
      Size.SMALL.toString();
    • valueOf:toString的逆方法,静态方法
      Size s = Enum.valueOf(Size.class, "SMALL");
    • values:静态方法,返回一个包含全部枚举值的数组。
      Size[] values = Size.values();
    • ordinal:返回enum声明中枚举常量的位置,从0开始计数。

第六章 接口与内部类

  1. 接口( interface ):接口不是类,而死对类需求的一组描述。接口中所有方法都自动属于 pubic,因此不必提供关键字public, 但在接口的实现中必须将其声明为public。接口中的域都将自动被设置为 public static final。接口中可以定义常量,但接口中决不能含有实例域,也不能在接口中实现方法.
    为了让类实现接口,通常需要:
    • 让类声明为实现给定的接口。使用implements关键字
      class Employee implements Comparable
    • 对接口中的所有方法进行定义。
      特性:
    • 接口不是类,不能使用new运算符。
    • 但可以声明接口的变量,接口变量必须引用实现了接口的类对象。
      Comparable x = new Employee();
    • 可以使用instanceof检查一个对象是否实现了某个特定的接口。
      if(anObject instanceof Comparable) {}
    • 接口允许被拓展
      public interface Powered extends Comparable{}
      使用 , 将实现的各个借口分隔开
      class Empliyee implements Cloneable, Comparable
  2. 对象克隆:默认的克隆操作是浅拷贝,会拷贝队形中所有属于数值或者基本类型的与。如果对象包含了子对象的引用,拷贝的结果就会使两个对象共享这部分信息。如果子对象是可变的,那就要重新定义clone方法以便实现深拷贝。
    对每一个类判断:
    • 默认的clone是否满足要求
    • 默认的clone是否能够条用哪个可变子对象的clone得到修补
    • 是否不应该使用clone
      要实现上述前两点,必须
    • 实现Cloneable接口
    • 使用public访问修饰符重新定义clone方法。
      即使clone默认操作满足要求,也要实现Cloneable接口,并将其重新定义为public,并调用super.clone();
      class Employee implements Cloneable{
      public Employee clone() throws CloneNotSsupportException{
      return (Employee)super.clone()
      }
      }
  3. 接口与回调( callback ):回调是一种常见的程序设计模式,在这种模式中,可以指出某个特定事件发生时应该采取什么动作。
  4. 内部类(inner class):是定义在另一个类中的类。使用内部类的原因
    • 内部类可以访问该类定义所在的作用域中的数据,包括私有数据
    • 内部类可以对同一个包中的其他类隐藏起来
    • 当想要定义一个毁掉函数且不想编写大量代码时,使用匿名( anonymous )内部类比较便捷。
      尽管内部类位于类中,但不意味着每个外部类都有一个内部类实例域。
      内部类对象总有一个隐式引用,指向创建它的外部类对象。
  5. 内部类的特殊语法规则:
    • OuterClassName.this表示外围类的引用。
    • OuterObject.new InnerClass(construction parameters):更加明确的编写内部对象的构造器。
    • OuterClass.InnerClass:这样引用内部类
  6. 局部内部类:是定义在类方法中的类,不能使用public或者private访问说明符进行声明。可以对外界世界完全隐藏,即使外部类的其他代码也不能访问。局部类不仅可以访问他们的外部类,还可以访问被声明为final的局部变量。
public void start(int interval, final boolean beep){
    class TimePrinter implements ActionListener{
        public void actionPerformed(ActionEvent event){
            if(beep) {...}
        }
    }

    ActionListener listener = new ActionListener();
}
  1. 匿名内部类( anonymous inner class ):如果只想穿件这个类的一个对象,就不用命名了。
public static void start(int interval, final boolean final){
    ActionListener listener = new ActionListener()
    {
        public void actionPerformed(ActionEvent event)
        {
            Date now = new Date();
            ...
        }
    }
}

通用语法格式为
new SuperType(construction parameters){
inner class methods and data
}
由于构造器必须和类名相同,而匿名类没有类名,这样,匿名类就不能有构造器。取而代之的是将构造器参数传递给超类( superclass )的构造器。尤其在内部类实现接口的时候,不能有任何构造参数。
8. 静态内部类:使用内部类仅仅为了把一个类隐藏在另一个类的内部,而不需要内部类引用外围类对象,为此,可以将内部类声明为 static。当然只有内部类可以声明为static。

展开阅读全文

没有更多推荐了,返回首页