Java核心技术(第8版) – 读书笔记 – 第4章

1、Knuth:算法+数据结构 = 程序,算法优先。
面向对象:数据优先。面向对象利于划分,隔离子模块,方便调试与开发。

2、类(class)是构造对象的模板。类->对象的过程叫做实例化(instance)。

3、对象一般将数据域(instance fields)封装(encapsulation)在对象内部,对外提供方法(method),用于整体操纵类的状态。

4、OOP原则一:封装是必须的,不能让外部直接操纵类内部数据对象。

5、OOP原则二:类可以扩展自另外一个类,称为继承(inheritance)。

6、OOP对象三特性:行为(method)、状态(内部state,fields)、标识(如何区分两个对象,如订单ID、学号等)

7、类之间的关系:
依赖( use-a ):Order需要使用Account的用户配送地址
聚合( has-a ):Order中包含若干个Item
继承 ( is-a ):DiscountOrder继承自Order,实现了一个打折策略

8、并不是所有类都具有面向对象特征,例如Math类的各个方法Math.sin()/Math.pow(),方法不会改变内部状态,因为这个类就没有内部状态的域fields。这种类也是允许的。

9、类(class)的构造函数(construct)负责构造、初始化对象(object)。

10、Date dt = new Date();
dt仅仅是一个Date对象的引用。
如果把dt赋值给另外一个Date变量dt1,则他们都指向的dt。

import java.util.Date;

public class Test
{
    public static void main(String [] args)
    {
        Date dt = new Date();
        Date dt2 = dt;
        System.out.format("Is dt and dt2 the same object, %b\n",(dt==dt2));
    }
}

11、Java中,对象引用默认为null,避免了C++中未初始化的指针未初始化则未随机值导致的各种内存错误。

12、GregorianCalendar继承自Calendar,提供了阳历相关的各种接口。

构造函数,可以指定当前时间或者指定时间

//Now
GregorianCalendar cal2 = new GregorianCalendar();
//Spcific the date: dooms day
GregorianCalendar cal = new GregorianCalendar(2012, Calendar.DECEMBER, 23, 0, 0);

访问器(Accessor):可以获取日历的某个域:年、月、日、小时。。。

//Show Month,注意月份是从0开始的,比如12月会输出11!
System.out.println(cal.get(Calendar.MONTH));
//获取当地时区一周的第一天是周几,例如中国就是返回1。
System.out.println(cal.getFirstDayOfWeek());

修改器(Mutator method):

cal.add() // 增加天、月等
cal.set(xxx) // 直接设置月、天等

在Calendar和Date之间转换:

Date dt = cal.getTime();

13、类java.text.DateFormatSymbols用于日期的格式化,可输出Locales所在地字符串的年、月、日。。周一。。。周日。。。

        //Print 周一 ... 周日
        for(String s:new DateFormatSymbols().getWeekdays())
        {
            System.out.println(s);
        }

14、自定义类推荐的格式:

class XX
{
    constructor1()...
    constructor2()...

    method1()
    method2()

    field1
    field2
}

15、如果类XXXTest.java引用了XXX.java,则javac XXXTest.java时候,会自动编译XXX.java

16、public:所有类都可以访问、private:只有类内部才能访问。

17、构造函数与类同名、有0+个参数、无返回值、new后触发。

18、类内部可用this指明自己,在参数与内部域同名的时候,经常实用。

19、公有的域更改器、域访问器,私有的域。

20、不要直接返回引用对象!这将破坏函数的封装性!

//不要直接返回对象,很危险!!
public Date getHireDay()
{
    return this.hireDay;
} 

//访问之前    System.out.println("name:"+e2.getName()+",salary:"+e2.getSalary()+",hire:"+e2.getHireDay());
//e2.getHireDay()之后会修改e2内部的hireDay对象!
e2.getHireDay().setTime(0);
//被修改了      System.out.println("name:"+e2.getName()+",salary:"+e2.getSalary()+",hire:"+e2.getHireDay());

上面的代码中,返回的Date对象可以被任意修改(因为Java中没有C++那种拷贝函数,每个对象都是引用)。

正确的做法是:在返回之前,先克隆!(拷贝出来一个完全新的对象!)

//正确的做法
public Date getHireDay()
{
    return (Date)this.hireDay.clone();
}

当然,这个前提是,你要纯粹的get方法,不希望别人修改。

21、一个类可以访问所有该类的对象!即同类访问对象,不受private限制。

这个在实现equals方法时候非常有用:

class Employee
{
    bool equals(Employee other)
    {
        return this.name.equals(other.name);
    }
....
    private String name;
}

22、如果一个公有方法需要由多个步骤实现(例如基于TCP的App协议)。那么应该把这些子步骤的方法设置为私有的。

23、final可用于修饰对象的实例域。

private String name; //用于基本类型,一旦name创建后就无法被修改了。

private Date day; //用于非基本类型(对象类型),该引用不会变,但类内部的域是可以变的。

24、如果数据域定义为static,则这个类的所有对象将共享这个数据域。

class XXX
{
    public XXX()
    {
        nextID++;
        this.id = nextID;
    }

    private static int nextID = -1;
    private int id;
}

如上的nextID,为类所有对象共享,递增的ID,没创建一个类时。

25、如果定义为public static final,则这个变量可以被其他类不构造改类对象,就直接实用。经常用于常量。

class Math
{
    public static final double PIE = 3.1415926;
}

也可以用于共享的资源,例如非常熟悉的System.out:

public class System
{
    public static final PrintStream out=...;
}

26、static用于方法上,成为静态方法。静态方法内部无法使用this。因此,静态方法不能操纵对象内的实例域。但可以操纵类静态域(static域).

27、静态方法应用的场景
(1)所需要的所有参数都通过参数显示提供了如Math.pow()。
(2)一个方法只需要访问类的静态域名。

28、回顾下C语言中的static意义
(1)函数内部的static变量,再次进入时不会被初始化。
(2)用于多文件,变量共享。
而C++又给static重行下了定义:
(1)类的静态(static)数据域,被所有该类的对象共享且只有一个。
C++的static意义和Java是一致的。不过C++用::访问,Java统一都用.访问。

29、工厂方法(Factory):
NumberFormat currencyFomartter = NumberFactory.getCurrencyInstance();
NumberFormat precentFomartter = NumberFactory.getPrecentInstance();

使用工厂方法的原因是:同一个构造函数,无法返回两个不同的子类,例如上面代码中:

30、main方法:虽然写在类中,但不合任何类绑定,也无法访问类的静态域。

public static void main()

31、每个类都可以有自己的main方法,但具体执行哪个main,取决于

java xxx 运行的是哪个类。

32、Java的参数调用总是传值,方法得到的所有参数都是参数值的一个拷贝,这个有点复杂:
(1)基本数据类型,int、double这种,在函数里的更改不会影响调用者。
(2)对象:传值传的是引用,这样依赖,在参数里面改变对象,会影响调用它传入的对象状态。
(3)对于String:函数里面对String参数的更改不会影响外面。这也是容易理解的。因为String指向的字符串是共享的,没有一定的对象。到了外面就被抛弃了,下面的代码将输出两次”aaa“,即没有成功更改String a。

public class Test
{
    
    public void TestString(String a)
    {   
        a = "aaa_aaa";
    }   

    public static void main(String [] args)
    {   
        Test t = new Test();
        String a = "aaa";
        System.out.println(a);
        t.TestString(a);
        System.out.println(a);
    }   
}

33、重载(overload):多个方法有相同的名字、不同的参数。但不允许有仅返回类型不同的函数。

34、如果在构造函数中没有给域赋值,则会被默认初始化为:0、False或者null。

35、如果没有在类中写构造函数,Java会提供默认的构造函数,将所有域按上面的0、false、null初始化。

36、可以不用构造函数,在类中显示对域初始化,如下:

class Emplyoee
{
    private String name="xxx";
}

甚至可以不一定是常量初始化,可以用函数:

class Emplyoee
{
    static int assignID()
    {
        return id++;
    }
    private String name=assignID();
}

37、尽量不要让函数的参数名和对象内的域名相同,以防隐藏起来。或者显示的指定this,例如:

class XXX
{
    public func(String name)
    {
        this.name = name;
    }
}

38、可以从一个构造函数中调用另外一个构造函数:将this(xxx.xxx)放在构造函数的第一位,如下:

public Employee
{
    public Employee(double s)
    {
        this("name", s);
        //......做其他事情
    }
}

39、Java还有初始化块机制,在类中匿名的包含块,只要对象被构造,这些块一定会被执行。这种方法不是必须的,一般可以通过其他构造方法替换掉。

public class Employee
{
    .....
    public Employee()
    {
    }
     
    //Init block
    {
        name = "aaa";
        salary = 10.0;
    }
    private String name;
    private double salary;
}

40、静态初始化块可以对static域初始化,第一次调用类的时候,static块执行。(放load Configure不错!)

public class Employee
{
    .....
    public Employee()
    {
    }
     
    //Static Init block
    static
    {
        id = 0;
    }
    static private int id;
}

41、java.util.Random对象,产生随机值。
int nextInt(int n) // 产生0~n-1 之间的随机数。

42、Java不需要析构函数(因为对象垃圾回收,自动释放)。如果用JNI调用了其他资源,或者数据库连接、文件句柄等。可以添加finalize方法,它在对象被回收前一定会被调用一次。建议:对于这些资源的释放,人工加一个close方法,确保人工关闭!

43、Java使用包(package)将类组织起来。

44、导入类有两种方法:
1、全路径写明
2、import
两种方法如下:

//全写,很讨厌
java.util.Date d = new java.util.Date();

//import
import java.util.Date;

//Another import
import java.util.*;

import要注意不要产生冲突,比如java.util.Date和java.sql.Date,此时,就必须写全路径了。

45、import时候,也会自动带入静态函数、常量。所以import java.lang.Math之后,就可以直接pow()了,不用再加Math了。

46、将类放在别的包下,需要在文件头部声明package:

package com.coder4.TestClass

public class TestClass
{
......
}

但是编译、运行的时候,有点不太一样:

#编译,用目录
javac com/coder4/TestClass.java
#执行,用包.名
java com.coder4.TestClass

47、如果函数、域没有写明public、private,则默认是包可用的,即这个包中其他类都可以访问、修改它。

class XXX
{
    //everyone in same pakcage can modifiy me
    String str;
}

48、可以把一堆类打成jar包,方便运行、安装。注意jar包依赖的其他class文件,一定要在path变量下。否则,就得用java -classpath xxx 指定了。

49、JDK文档注释:javadoc,写符合它的注释,将来可以生成HTML样子的文档!

50、javadoc: /**开始 */结束

51、javadoc类注释:

/**
 * JavadocDemo.java,一个显示JavaDoc注释的Applet
 * <p>注意这只是HelloApplet的一个带注释的版本</p>
 * @see java.applet.Applet
 * @see javax.swing.Japplet
 */

 public class JavadocDemo extends Applet{

52、方法注释

/**
 * A method
 * @param a a is xxxx
 * @param b b is xxxx
 * @return xxxxx
 */
public String method(int a, int b)

53、域注释(成员变量)

/**
 *  The "HEARTS" var
 */
public static final int HEARTS = 1;

54、其他注释:
@author 作者
@version 版本
@since 自xx版本
@desprecated text 废弃
@see xxx
@see 的xxx又三种情况
(1)@see com.xx.xxClass#Method -> 直接引用其他类的方法
(2)@see <a href="http://www.coder4.com" >链接</a> -> 指向链接
(3)@see "Text" -> 纯指向文本

55、包注释:方法一:package.html命名的页面。方法二:package-info.Java的文件,用javadoc注释。

56、从注释生成javadoc网页。

javadoc -d docDir *.java

57、类设计技巧:
(1)数据域一定要私有
(2)一定要对局部数据初始化
(3)不要在类中使用过多的基本数据类型(比如User包含street、city、postcode,不如让他包含一个Address,后者再包含street、city等细碎的信息)
(4)不是所有类都要getter和setter,如果不要更改状态的,就不要加上。
(5)类名、方法要体现出用途
(6)功能过大的类要拆分、但也不可拆分太细!

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *