java面向对象编程基础-读书笔记

时间:2022-09-20 10:09:23
一.基础
*Java语言的运行机制
.java 源文件 -- 编译 --> .class 字节码文件 JVM 中解释执行 --> 真正的机器指令
先编译,后解释, JVM 事实上是一个软件 , 这个软件为 Java 程序模拟出一个统一的运行环境。 Java 程序只 需要适应这个虚拟的环境,而与底层真正的硬件环境及操作系统环境无关。换句话说, JVM 的作用在于,它屏蔽了底层不同平台的差异。
*JRE与JDK
JRE ,是 Java Runtime Environment 的缩写,指的是 Java 的运行环境。
JDK ,是 Java Development Kit , Java 开发工具包。
JDK = JRE + 工具(编译器、调试器、其他工具„„) + 类库
*环境变量
JAVA_HOME 环境变量,表示的是 Java 的安装目录。
PATH 环境变量。
CLASSPATH 是用来指示编译器和 JVM 去哪个目录寻找 .class 文件。
一个 .java 文件中定义的 每一个类,编译后都会对应的生成一个和类名完全一样的 .class 文件。 这个 .class 文件不是可执行文件,用文本编辑器也无法正常打开。
*八种基本类型

分别为 byte short int long float double char boolean
java面向对象编程基础-读书笔记
java面向对象编程基础-读书笔记
java面向对象编程基础-读书笔记

*Java 自动类型提升的规则如下:
1. 如果运算数中存在 double ,则自动类型提升为 double
2. 如果运算数中没有 double 但存在 float ,则自动类型提升为 float
3.如果运算数中没有浮点类型,但存在long,则自动类型提升为long
4.其他所有情况,自动类型提升为int

二,对象、类、封装继承多态
*对象创建过程
1. 分配空间,计算机中的对象,本质上是内存中的一块数据。因此,在计算机中创 建一个对象,就意味着必须在内存中为一个对象分配一块数据区域。 此外,在分配空间的同时,会把对象所有的属性设为默认值。这也是为什么实例变量不 用赋值,就有默认值的原因。
2. 初始化属性, 如果在定义实例变量的同时,为实例变量进行了赋值。则赋值语句在这一步骤执行。
3. 调用构造方法,当分配空间和初始化属性完成之后,最后一步是对该对象调用构造方法。

*有了继承关系之后,对象创建过程如下:
1.分配空间。要注意的是,分配空间不光是指分配子类的空间,子类对象中包含的父 类对象所需要的空间,一样在这一步统一分配。在分配空间的时候,会把所有的属性值都设为默认值。
2.递归的构造父类对象。
3.初始化本类属性。
4.调用本类的构造方法。

* super 关键字
用法一: super 用在构造方法上
class Child extends Parent{
     public Child(String str){
                      super(str);
          System.out.println("Child(String)");
     }
}

用法二: super 用作引用
class Parent{
     public void m(){
          System.out.println(“m in Parent ”);
     }
}

class Child extends Parent{
     public void m1 (){
          this.m();
     }
     public void m2(){
          super.m();
     }
}

*多态语法特性
一个引用的引用类型和对象类型未必完全一致,对象类型可以是引用类型的子类。 当我们看到一个引用时,一方面要考察这个引用的引用类型,另一方面还要考察这个 引用中所存储的对象的类型,这两个类型可能是不同的。
对象类型永远不变
只能对一个引用调用其引用类型中定义的方法
运行时,根据对象类型调用子类覆盖之后的方法
“覆盖”的定义为:子类用特殊的方法实现替换掉父类的一般的实现。

*继承间类型转换
子类的引用可以直接赋值给父类引用。(这是多态的基本用法)
父类的引用赋值给子类引用,必须强制类型转换,并有可能在运行时得到一个类型 转换异常。

* instanceof
避免类型转换异常的发生,是 一个二元运算符,用来组成一个布尔表达式。用来判断某个引用所指向的对象是否和某个类型兼容。基本语法: 引用 instanceof 类名

*多态的作用
把多态用在方法的参数类型上, 在定义方法时,可以把形参定义为父类类型的引用,而调用方法时,完全可以把子类类型的对象作为实参。很显然, 形参为父类类型的方法更加通用,能够接受更多种不同子类类型的实参对象。
把多态用在方法的返回值类型上,把返回值类型定义为父类,会使得方法更加的通用。在方法的实现中,我们可以返回任何一个子类的对象。

三.static、final、abstract

java面向对象编程基础-读书笔记

在非静态方法中,无论方法或属性是否是静态的,都能够访问;
在静态方法中,只能访问静态属性和方法。
静态方法只能被静态方法覆盖,非静态方法只能被非静态方法覆盖。
静态方法可以用类名直接调用,没有多态


类加载就是把 .class 文件读入 JVM 的过程。当 JVM 第一次遇到某个类时, 会通过 CLASSPATH 找到相应的 .class 文件,读入这个文件并把类的信息保存起来,这个过程叫做类加载。 类加载完毕之后,创建对象时,会调用对象的构造方法。 类加载除了读取类的信息,执行静态初始化代码块之外,还会为类的静态属性分配空间,并初始化其值为默认值。


final变量,用 final 修饰的变量则称为常量,一旦赋值, 其值不能改变的变量。
由于 static 属性是在类加载的时候分配空间的,因此静态的 final 属性不能 在构造方法中赋值。可以选择在定义这个属性的时候赋值,或是在静态初始代码块中为这个属性赋值。
class MyValue6{
     final static int value = 200;
}

final方法, 表示该方法不能被子类覆盖。
class Super{
     public void m1(){}
     public final void m2(){}
}
class Sub extends Super {
     public void m1(){} // 能够覆盖父类方法
     public void m2(){} // 编译出错!无法覆盖父类方法!
}
final类, 表示这个类不能被继承。
final class MyClass{ }
class Sub extends MyClass{} // 编译出错,无法继承一个final

*抽象方法的语法特征
1. 抽象方法只有声明,没有实现。实现的部分用分号表示。
2. 一个拥有抽象方法的类必须是抽象类。
3. 子类继承抽象类,要么也成为抽象类,要么就必须实现抽象类中的所有抽象方法。
“覆盖”的定义为:子类覆盖父类的抽象方法时,并不是用一个特殊的实现替换一个一般的实现,而是在父类没有方法实现的情况下,子类给出一个方法的实现。


四.接口
*接口的特点
1 、 所有属性都是公开静态常量
2 、 所有方法都是公开抽象方法
interface MyInterface{
     public static final int VALUE1 = 100;
     public static final int VALUE2 = 200;
     public abstract void m1();
     public abstract void m2(int n);
},同
interface MyInterface{
     int VALUE1 = 100;
     int VALUE2 = 200;
     void m1();
     void m2(int n);
}

注意:
1.一个类实现接口,如果不希望这个类作为抽象类,则应该实现接口中定义的所有方法。
2.接口中所有的方法都是公开方法。因此,在实现接口中的方法时,实现类的方法也 必须写成公开的。

接口和接口之间可以多继承;一个类在继承一个父类的同时,还能够实现多个接口。

*接口的作用
多继承, 用接口实现的多继承,则不会破坏类之间树状结构的简单性。 这是因为这棵树是由类之间形成的,是事物主要类型所组成的关系。一个类实现再多的接口, 有再多的次要类型,也不会改变其主要类型之间的树状结构。
解耦合, 定义一个接口,可以看作是定义了一个标准。它只定义了,一个对象应该具有哪些方法,而丝毫没有定义对象如何实现这些方法。方法的实现统统交给接口的实现类来完成。这样,接口的出现,就阻隔了接口使用者和接口实现者之间的耦合关系。当接口实现者变化的时候,对接口使用者不产生任何影响。

interface Bulb{    
     void shine();
}
class RedBulb implements Bulb {
     public void shine(){
          System.out.println( Shine in Red );
     }
}

class YellowBulb implements Bulb {
     public void shine(){
          System.out.println( Shine in Yellow );
     }
}
class GreenBulb implements Bulb {
     public void shine(){
         System.out.println( Shine in Green );
     }
}

class Lamp{
           private Bulb bulb;
     public void setBulb( Bulb bulb ){
          this.bulb = bulb;
     }
     public void on(){
          bulb.shine();
     }
}

public class TestLamp{
     public static void main(String args[]){
          Lamp lamp = new Lamp();
          Bulb b1 = new RedBulb();
          lamp.setBulb(b1);
          lamp.on();
          Bulb b2 = new GreenBulb();
          lamp.setBulb(b2);
          lamp.on();
     }
}
Lamp 类的 bulb 属性被改为接口类型 Bulb ,从而, Lamp 类与具体的实现类之间用 Bulb 接口分离开了。当 Lamp 类希望把 bulb 属性由 RedBulb 对象变更为 GreenBulb 对象时,不需要修改任何自身代码。

*接口回调
java面向对象编程基础-读书笔记
例如,为了定义排序规则, Sun 公司定义了一个接口: java.lang.Comparable 。这个接口 用来表示一个对象的排序规则。我们编写的类应该实现这个接口。 Comparable 接口中只有一个方法: compareTo 方法,实现这个方法,就能规定两个对象如何比较大小。假设程序员要创建一个 Student 类,希望用 java.util. Arrays.sort() 对一些 Student 对象进行排序,则要求 Student 类实现 Comparable 接口。示意图如下:
java面向对象编程基础-读书笔记
class Student implements Comparable<Student>{//泛型
     int age;
     String name;
     public int compareTo(Student stu){
          // ...
     }
}
java面向对象编程基础-读书笔记

由系统为我们提供 A 类和 I 接口,我们负责编写 B 类来实现 I 接口。 A 类通过对 I 接口中方法的调用,利用多态,来调用我们所写的 B 类的方法。

五.Object类与常用类、内部类
*Object类
- finalize, 在对象被垃圾回收的时候调用,
                    System.gc() 调用这个方法,就相当于通知 JVM ,程序员希望能够进行垃圾回收。
-getClass, 返回对象的实际类型。
-equals, 判断两个对象内容是否相等。
-toString, 方法的返回值是某个对象的字符串表现形式。

覆盖 equals 方法,样例
public boolean equals(Object obj){
if (obj == this) return true; // 判断 obj 是否和 this 相等,保证自反性
if (obj == null) return false; // 判断 obj 是否为 null ,保证最后一条准则  

// 判断两个对象的实际类型是否相等,
// 如果不相等,则说明比较的是两个不同种类的对象,应当返回 false
if (obj.getClass() != this.getClass()) return false;

// 强制类型转换
// 由于之前已经使用 getClass 判断过实际类型,因此这里强转是安全的
Student stu = (Student) obj;
// 判断每个属性是否相等
// 对于基本类型的属性用“ == ”比较,对象类型的属性用 equals 比较
if (this.age == stu.age && this.name.equals(stu.name) )
     return true;
else return false;
}

*包装类
java面向对象编程基础-读书笔记
java面向对象编程基础-读书笔记
int->Integer,
int i1 = 10;
Integer ii1 = new Integer(i1);
Integer->int,
int i2 = ii1.intValue();
String->包装类,
String str = 123 ;
Integer ii2 = new Integer(str);
包装类->String,
String str2 = ii2.toString();
int->String,
int i = 10;
String str3 = String.valueOf(i);或
String str4 = “” + i;
String->int,
int i = Integer.parseInt( 123 );
另,
Integer myInteger = 10;
int i = myInteger;
以下代码运行时会产生 NullPointerException异常
public class TestAutoBoxing {
     public static void main(String[] args) {
          Integer myInteger = null;
          int i = myInteger;
     }
}

*内部类: 成员内部类、静态内部类、局部内部类、匿名内部类
-成员内部类, 必须与外部类某一个对象相关联,因此成员内部类中不能定义静态方法。
- 静态内部类,
     成员内部类中不能定义静态方法;成员内部类中能够访问外部类的所有静态以及非静态的成员;
     静态内部类中可以定义静态方法,静态内部类中只能访问外部类的静态成员(即访问外部类的静态属性或者调用外部类的静态方法。)
-局部 内部类, 在局部内部类中能够访问外部类的局部变量,但是要求该变量必须是 final 的。

interface Teacher{
     void teach();
}

public class TestTeacher{
     public static void main(String args[]){
          Teacher t = getTeacher(10);
          t.teach();
     }

     public static Teacher getTeacher(int n) {
          class Tom implements Teacher{
               public void teach(){
                    System.out.println( Tom teach );
               }
          }

          class Jim implements Teacher{
              public void teach(){
                    System.out.println( Jim teach );
              }
          }
     
         if (n == 10)  return new Tom();
         else return new Jim();
     }
}


public class TestTeacher{
     public static void main(String args[]){
          Teacher t = getTeacher(10);
          t.teach();
     }

     public static Teacher getTeacher(int n){
          if (n == 10) return new Tom();
          else return new Jim();
     }

     //反例,强耦合
      public static void main(String args[]){
         Teacher t = new Tom();
         t.teach();
     }
}
getTeacher 方法的外部,无法访问到 Tom 类和 Jim 类,也就无法直接创建出 Teacher 接口的实现类。也就是说,程序员只能通过调用 getTeacher 方法来获得 Teacher 对象, 而无法直接创建某个实现类的对象。这样,就能够实现“强制弱耦合”,即强制程序员必须要利用 Teacher 接口来写程序,从而实现弱耦合。

-匿名 内部类, 是一种特殊的局部内部类。
     一个局部内部类满足这样两个特点:
          1 、该 内部类继承自某个类或者实现某个接口;
          2 、该内部类在整个方法中只创建了一个对象。
如,

public static Teacher getTeacher(int n){
    class Jim implements Teacher{      //Jim 依然采用局部内部类的写法
          public void teach(){
               System.out.println( Jim teach );
          }
     }

     // 匿名内部类
     if (n == 10) return new Teacher(){
          public void teach(){
               System.out.println( Tom teach );
          }
     };
     else return new Jim();
}
     上面这段代码,把 Tom 类修改成了一个匿名内部类。首先, return 语句返回了一个 Teacher 类型的对象。由于 Teacher 是一个接口,不能创建对象,因此所谓的 new Teacher() ,实际上是创建了一个实现 Teacher 接口的类的对象。
     那创建的对象是什么类型的呢?这个实现类没有名字,这也就是为什么这种语法要叫做 “匿名”内部类的原因。而且正因为这个类没有名字,因此没有办法通过 new 类名 () 的方式创建对象。也就是说,匿名内部类没有名字,并且只能在一次方法调用中创建一个匿名内部类的对象。

      new 接口名 () { 实现接口的代码 } ;
     以上的代码首先用一对 {} 来实现了接口,然后用 new 关键字创建出了一个对象。这句话创建的绝不是接口的对象(接口是特殊的抽象类,无法创建对象),而创建的是一个实现了接口的,没有名字的内部类的对象。


六.集合框架
java面向对象编程基础-读书笔记

要掌握每种集合接口,就要重点掌握集合接口的这几个方面:
1 、 接口的特点
2 、 接口中定义的基本操作
3 、 该集合如何遍历
4 、 接口的不同实现类,以及实现类之间的区别

* Collection
- 特点是元素是 Object 。遇到基本类型数据,需要转换为包装类对象。
-基本操作
      boolean add(Object o)
     boolean contains(Object o)
     boolean isEmpty()
     Iterator iterator()
     boolean remove(Object o)
     void clear()
     int size()

* List
- 特点:元素是对象,并且元素有顺序,可以重复。 元素的所谓“顺序”,指的是每个元素都有下标。
-基本操作
      boolean add(Object o) / void add(int index, Object element)
      Object get(int index) / Object set(int index, Object element)
     int indexOf(Object o)等
-遍历:for、迭代遍历,如
      public class TestArrayList {
     public static void main(String args[]){
          List list = new ArrayList();
          list.add("hello");
          list.add("world");
          list.add("java");
          list.add("study");

          Iterator iter = list.iterator();

          while(iter.hasNext()){
              Object value = iter.next();
              System.out.println(value);
          }
     }
  }
-实现类:Array、LinkedList、Vector
java面向对象编程基础-读书笔记
java面向对象编程基础-读书笔记

* Set
- 特点: 元素不可以重复,无顺序。 元素没有下标的概念。
-基本操作,参考Collection
-遍历:只能迭代遍历,参考上述 Set set = new HashSet();
-实现类,HashSet、LinkedHashSet
++HashSet, HashSet 只有在 hashCode 返回值冲突的时候才会调用 equals 方法进行判断。也就是说,两个对象,如果 hashCode 没有冲突, HashSet 就不会调用 equals 方法判断而直接认为这两个对象是不同的对象。
     如果要正常使用 HashSet 存放对象,为了保证对象的内容不重复,则要求这个对象满足:
     1.覆盖 equals 方法。要求相同的对象,调用 equals 方法返回 true
     2.覆盖 hashCode 方法。要求相同对象的 hashCode 相同,不同对象的 hashCode 量不同。
++ LinkedHashSet ,按先后顺序加入元素。

* Map
- 特点: 元素是“键值对”, 键不可以重复,值可以重复
-基本操作,
      Object get(Object key)
     put(Object key, Object value)
     remove(Object key)
     Set keySet()
     Collection values()
     containsKey / containsValue
     size()
     isEmpty()
     clear()
     entrySet
-遍历:用实现类HashMap举例,
import java.util.*;
public class TestMap {
     public static void main(String args[]){
          Map map = new HashMap();
          map.put("2006", "Italy");
          map.put("2002", "Brazil");
          map.put("1998", "France");
          map.put("1994", "Brazil");
     }
}
++ 键遍历与键值遍历
Set set = map.keySet();
Iterator iter = set.iterator();
while(iter.hasNext()){
     System.out.println(iter.next());
}
//
Set set = map.keySet();
Iterator iter = set.iterator();
while(iter.hasNext()){
     Object key = iter.next();
     Object value = map.get(key);
     System.out.println(key + "--->" + value);
}
++值 遍历
Collection conn = map.values();
Iterator iter = conn.iterator();
while(iter.hasNext()){
     System.out.println(iter.next());
}
++ 利用 Map.Entry 进行遍历
Set set = map.entrySet();
Iterator iter = set.iterator();
while(iter.hasNext()){
     Map.Entry entry = (Map.Entry) iter.next();
     System.out.println(entry.getKey() + "-->" + entry.getValue());
}
-实现类,HashMap、LinkedHashMap
java面向对象编程基础-读书笔记


* Comparable与排序
- java.util 包中,有一个 Collections 类, 类中所有的方法都是静态方法,如 Collections.sort 方法。
- TreeSet TreeMap
++ Set 接口有一个子接口: SortedSet ,对应的实现类: TreeSet
++Map 接口有一个子接口: SortedMap ,对应的实现类: TreeMap
++ 要求放入 TreeSet 接口中的对象都必须实现 Comparable 接口。
如,
class Student implements Comparable<Student>{
     int age;    
     String name;
     public Student() {
     }
     public Student(String name, int age) {
         this.name = name;
         this.age = age;
     }
     public int compareTo(Student stu){
          if (this.age > stu.age){
              return 1;
          }else if (this.age < stu.age){
               return -1;
          }else {
               return 0;
          }
     }
}

public class TestTreeSet {
     public static void main(String args[]) {
          Set set = new TreeSet();
          set.add(new Student("Tom", 18));
          set.add(new Student("Jim", 17));
          set.add(new Student("Jerry", 20));
     
          Iterator iter = set.iterator();
          while(iter.hasNext()){    
               Student stu = (Student) iter.next();
               System.out.println(stu.name + " " + stu.age);
          }
     }
}

* 5.0新特性:foreach循环
String[] ss = new String[]{“hello”, “ world ”, “java”};
for(String obj : ss){
     System.out.println(obj);
}

* 5.0新特性:泛型
-定义, List 接口定义 为: List<E> ,其中, E 就表示 List 的泛型。
-泛型与多态, 类型可以有多态,泛型不能够有多态, 不同泛型的引用之间 不能相互赋值。
List<Dog> dogList = new ArrayList<Dog>();
List<Animal> aniList = dogList; //! 编译出错!
在第一行代码中 ,类型里有多态(把 ArrayList 赋值给 List ),但是泛型是一样的(均是 Dog 的 泛型)。
在第二行代码中 ,类型中没有多态( List 是相同的),而泛型有多态(一个是 Dog 的泛型,一个 是 Animal 的泛型)。
-自定义泛型化类型
class Pair<T>{
     private T valueA;
     private T valueB;

     public T getValueA() {
          return valueA;
     }

     public void setValueA(T valueA) {
          this.valueA = valueA;
     }

     public T getValueB() {    
          return valueB;
     }

     public void setValueB(T valueB) {
          this.valueB = valueB;
     }
}

public class TestPair {
     public static void main(String[] args){
          Pair<Dog> p = new Pair<Dog>();
          p.setValueA(new Dog());
          p.setValueB(new Dog());
          p.setValueA(new Cat()); // 编译出错!
     }
}


七.异常处理
*异常分类
|-Throwable,所有错误的父类
     |-Error,严重的底层错误,无法处理
     |-Exception,异常,异常处理的主要对象
          |-RuntimeException的子类,未检查异常,可以避免,可处理可不处理
          |-非RuntimeException的子类,已检查异常,无法避免,必须处理
import java.util.*;
import java.io.*;
import java.sql.*;
public class TestException{
     public static void main(String[] args) {
          Scanner sc = new Scanner(System.in);
          int i = sc.nextInt();
          System.out.println("main 1");
          ma(i);
          System.out.println("main 2");
     }

     static void ma(int i) {
         try{
               System.out.println("ma 1");
               mb(i);
               System.out.println("ma 2");
          }catch(IOException ioe){
               System.out.println( IOException );
          }catch(SQLException sqle){
               System.out.println( SQLException );
          }catch(Exception e){
               System.out.println( Exception );
          } finally{
              System.out.println("in finally of ma");
          }
     }

     static void mb(int i) throws IOException, SQLException{                                                                                                   
          System.out.println("mb 1");
          mc(i);
          System.out.println("mb 2");
     }

     static void mc(int i) throws FileNotFoundException,
EOFException, SQLException{
          System.out.println("mc 1");
          if (i==0) throw new NullPointerException();
          if (i==1) throw new java.io.FileNotFoundException();
          if (i==2) throw new java.io.EOFException();
          if (i==3) throw new java.sql.SQLException();
          System.out.println("mc 2");
     }
}

*throw,表明抛出一个错误,语法throw+Throwable对象,如
     throw new NullPointerException();
*throws,声明抛出异常,上述代码
*try-catch,捕获异常
*finally,无论程序执行时是否发生异常,最终都会被执行
*异常与方法覆盖, 子类的覆盖方法不能比父类的被覆盖方法抛出更多的异常。
*Exception类getMessage方法,返回该方法的详细信息。
*Exception类printStackTrace方法, 在标准错误输出上打印出产生异常时的方法调用栈的信息。
如,
import java.util.*;
import java.io.*;
import java.sql.*;
public class TestExceptionArgs{
     public static void main(String args[]){
          Scanner sc = new Scanner(System.in);
          int n = sc.nextInt();
          ma(n);
     }

     public static void ma(int n) {
          try{
               mb(n);
          }catch(Exception e){
                                e.printStackTrace();
               System.out.println(e.getMessage());
          }
     }

     public static void mb(int n) throws Exception{
         if (n == 0) throw new SQLException("n==0");
         if (n == 1) throw new EOFException("n==1");
        if (n == 2) throw new FileNotFoundException("n==2");
     }
}

*自定义异常
class MyException1 extends Exception{ // 自定义已检查异常
     public MyException1(){}
     public MyException1(String str){
          super(str);
     }
}
class MyException2 extends RuntimeException{ // 自定义未检查异常
            public MyException2(){}
     public MyException2(String str){
          super(str);
     }
}
public class TestMyException{
     public static void main(String args[]){
          Scanner sc = new Scanner(System.in);
          int n = sc.nextInt();
          ma(n);
     }

     public static void ma(int n) {
          try{
               mb(n);
          }catch(MyException1 e){
               e.printStackTrace();
          }catch(Exception e){
               e.printStackTrace();
          }
     }

     public static void mb(int n) throws MyException1{
          if (n == 0) throw new MyException1();
          if (n == 1) throw new MyException2();
     }
}

八.多线程
*基本常识
- Java 代码是运行在 JVM 中的, 对于某个操作系统来说,一个 JVM 就相当于一个进程。而 Java 代码不能够越过 JVM 直接与操作系统打交道,因此, Java 语言中没有多进程的概念。 Java 中的并发,采用的是线程的概念。简单的来说,一个操作系统可以同时运行多个程序,也就是说,一个系统并发多个进程;而对于每个进程来说,可以同时运行多个线程,也 就是:一个进程并发多个线程。-- 宏观上并行,微观上串行
- 线程运行的条件: CPU 、代码、数据,数据在 堆空间共享,栈空间独立。

*两种代码实现方式,Thread类、Runnable接口
class MyThread1 extends Thread{
     public void run(){
          for(int i = 1; i<=1000; i++){
               System.out.println(i + " $$$");
          }
     }
}

class MyRunnable2 implements Runnable{
     public void run(){
          for(int i = 1; i<=1000; i++){
               System.out.println(i + " ###");

          }
     }
}

public class TestThread{
     public static void main(String args[]){
          Thread t1 = new MyThread1();
          Runnable target = new MyRunnable2();
          Thread t2 = new Thread(target);

          t1.start();
          t2.start();
     }
}

*线程状态
java面向对象编程基础-读书笔记
初始状态:创建一个线程对象,而没有调用这个线程对象的 start() 方法时,此时线程处于初始状态。
可运行状态 :线程已经为运行做好了完全准备,只等着获得 CPU 来运行。 调用 start() 方法之,进入此状态。
运行状态:处于这种状态的线程获得了 CPU 时间片,正在执行代码。
终止状态:当一个线程执行完了 run() 方法中的代码,该线程就会进入终止状态。
阻塞状态: 如果线程需要与 JVM 外部进行数据交互(如等待用户输入、读写文件、网络传输等,最典型的是 等待I/O ),当数据传输没有完成时,线程即使获得 CPU 也无法运行,因此进入阻塞状态。
++sleep
方法签名
public static void sleep(long millis) throws InterruptedException
应用样例
class MyThread1 extends Thread{
     public void run(){
          for(int i = 1; i<=1000; i++){
               System.out.println(i + " $$$");
                                try{
                                             Thread.sleep(200);
                                }catch(InterruptedException e){}
          }
     }
}

++join
方法签名
public final void join() throws InterruptedException
public final void join(long millis) throws InterruptedException
应用样例
class MyThread1 extends Thread{
     public void run(){
          for(int i = 0; i<100; i++){
               System.out.println(i + " $$$");
          }
     }
}

class MyThread2 extends Thread{
          Thread t;
     public void run(){
           try{
                              t.join(1000);
                      }catch(Exception e){}
          for(int i = 0; i<100; i++){
               System.out.println(i + " ###");
          }
     }
}

public class TestJoin{
     public static void main(String args[]){
          MyThread1 t1 = new MyThread1();
          MyThread2 t2 = new MyThread2();
           t2.t = t1;
          t1.start();
          t2.start();
     }
}
锁池状态: 运行中的线程,运行到某个同步代码块,但获得不了对象的锁标记时,进入锁池状态。在锁池状态的线程,会一直等待某个对象的互斥锁标记。 当对象的锁标记被某一个线程释放之后,其他在锁池状态中的线程就可以获得这个对象的锁标记。 之后进入可运行状态,等待获得 CPU 时间片,运行代码。

++synchronized与同步方法
样例1
class MyStack{
     char[] data = {'A', 'B', ' '};
     int index = 2;
           private Object lock = new Object();
     public void push(char ch){
                     synchronized(lock){
               data[index] = ch;
               try{
                    Thread.sleep(1000);
               }catch(Exception e){}
               index ++;
                      }
     }

     public void pop(){
                      synchronized(lock){
               index --;
               data[index] = ' ';
                      }
     }

     public void print(){
          for(int i = 0; i<data.length; i++){
               System.out.print(data[i] + "\t");
          }
          System.out.println();
     }
}

样例2,
class MyStack{
     char[] data = {'A', 'B', ' '};
     int index = 2;
     public void push(char ch){
                     synchronized(this){     //类对象本身,具有互斥锁标记
               data[index] = ch;
               try{
                    Thread.sleep(1000);
               }catch(Exception e){}
               index ++;
                      }
     }

     public void pop(){
                      synchronized(this){
               index --;
               data[index] = ' ';
                      }
     }

     public void print(){
          for(int i = 0; i<data.length; i++){
               System.out.print(data[i] + "\t");
          }
          System.out.println();
     }
}

样例3,
class MyStack{
     char[] data = {'A', 'B', ' '};
     int index = 2;
     public  synchronized void push(char ch){//同步方法
               data[index] = ch;
               try{
                    Thread.sleep(1000);
               }catch(Exception e){}
               index ++;
     }

     public  synchronized void pop(){
               index --;
               data[index] = ' ';
     }

     public void print(){
          for(int i = 0; i<data.length; i++){
               System.out.print(data[i] + "\t");
          }
          System.out.println();
     }
}

锁池状态: t1 线程调用 a 对象的 wait 方法, t1 线程会暂时释放自己拥有的 a 对象的锁标记,进入等待状态。( 调用对象 wait 方法,前提是线程已经获得这个对象的锁标记,反之 会产生异常。 t2线程 调用 a对象的notify/notifyAll 方法,会让t1线程被唤醒,进入锁池状态。
样例代码,生产者/消费者问题
class MyStack{
     private char[] data = new char[5];    
     private int index = 0;

     public char pop(){
          index -- ;    
          return data[index];
     }

     public void push(char ch){
          data[index] = ch;
          index++;
     }

     public void print(){
          for (int i=0; i<index; i++){
               System.out.print(data[i] + "\t");
          }
          System.out.println(); }

     public boolean isEmpty(){
          return index == 0;
     }

     public boolean isFull(){
          return index == 5;
     }
}


class Consumer extends Thread{
     private MyStack ms;
    
     public Consumer(MyStack ms) {
          this.ms = ms;
     }

     public void run(){
          while(true){

               // 为了保证 push pop 操作的完整性, 必须加 synchronized
               synchronized(ms){

               // 如果栈空间空,则 wait() 释放 ms 的锁标记
                    while(ms.isEmpty()){
                         try {
                              ms.wait();
                         } catch (InterruptedException e) {
                              e.printStackTrace();
                         }
                    }

                    char ch = ms.pop();
                    System.out.println("Pop " + ch);
                    ms.notifyAll();
               }
        
               //push 之后随机休眠一段时间
               try {
                    sleep( (int)Math.abs(Math.random() * 100) );
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
          }
     }
}

// 生产者
class Producer extends Thread{
     private MyStack ms;
     public Producer(MyStack ms) {
          this.ms = ms;
     }
    
     public void run(){
          while(true){

               // 为了保证 push pop 操作的完整性, 必须加 synchronized
               synchronized(ms){
                    // 如果栈空间已满,则 wait() 释放 ms 的锁标记
                    while(ms.isFull()){
                         try {
                              ms.wait();
                         } catch (InterruptedException e) {                                                                                             
                          e.printStackTrace();
                     }
                }

                    ms.push('A');
                    System.out.println("push A");
                    ms.notifyAll();
               }

               //push 之后随机休眠一段时间
               try {
                    sleep( (int)Math.abs(Math.random() * 200) );
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
         }
    }
}

public class TestWaitNotify {
     public static void main(String[] args) {
          MyStack ms = new MyStack();
          Thread t1 = new Producer(ms);
          Thread t2 = new Consumer(ms);
          t1.start();
          t2.start();
     }
}

九.IO框架
*I/O分类
按方向分类,分为输入流和输出流
按数据单位分类,分为字节流和字符流
按功能分类,分为节点流和过滤流

InputStream<-FileInputStream,OutputStream<-FileOutputStream
import java.io.*;
public class TestInputStream{
     public static void main(String args[])  throws Exception{
          FileInputStream fin = new FileInputStream("abc.txt");
          int ch = 0;
          while( (ch=fin.read()) != -1){
               System.out.print((char)ch);
          }
          fin.close();
     }
}


import java.io.*;
public class TestInputStream{
     public static void main(String args[]) throws Exception{
          FileInputStream fin = new FileInputStream("abc.txt");
          byte[] bs = new byte[6];
          int len = 0;
          while( (len=fin.read(bs))!=-1){
               for(int i = 0; I < len; i++){
                    System.out.print((char)bs[i]);
               }

               System.out.println();
          }
          fin.close();
     }
}


import java.io.*;
public class TestInputStream{
     public static void main(String args[]) {
          FileInputStream fin = null;
          try{
               fin = new FileInputStream("abc.txt");
               byte[] bs = new byte[6];
               int len = 0;
               while( (len=fin.read(bs))!=-1){
                    for(int i = 0; i<len; i++){        
                         System.out.print((char)bs[i]);
                    }
                    System.out.println();
              }
          }catch(Exception e){
               e.printStackTrace();
          }finally{
               if(fin!=null)
                    try{
                         fin.close();
                    }catch(IOException e){
                         e.printStackTrace();
                    }
          }
     }
}


DataInputStream,DataOutputStream
import java.io.*;
public class TestDataStream{
     public static void main(String args[]) throws Exception{
          //1. 创建节点流
          FileOutputStream fout = new FileOutputStream("pi.dat");
          //2. 封装过滤流
          DataOutputStream dout = new DataOutputStream(fout);
          //3. 写数据
          dout.writeDouble(3.14);
          //4. 关闭外层流
          dout.close();

          // 创建节点流
          FileInputStream fin= new FileInputStream ("pi.dat");
          // 封装过滤流
          DataInputStream din = new DataInputStream(fin);
          // 读数据
          double pi = din.readDouble();
          // 关闭外层流
          din.close();

          System.out.println(pi);
     }
}


BufferedInputStream, BufferedOutputStream

import java.io.*;
public class TestBufferedStream{
     public static void main(String args[]) throws Exception{
          String data = "Hello World";
          byte[] bs = data.getBytes();

          //1. 创建节点流
          FileOutputStream fout  = new FileOutputStream("test.txt");

          //2. 封装过滤流
          BufferedOutputStream bout = new BufferedOutputStream(fout);

          //3. 写数据
          bout.write(bs);

          //4. 关闭外层流
          bout.close();
     }
}

序列化与transient
import java.io.*;
class Student implements Serializable{
     String name;
     transient int age;// 不参与序列化
     public Student(String name, int age) {
          this.name = name;
          this.age = age;
     }

}

public class TestSerializable {
     public static void main(String[] args) throws Exception {
          Student stu1 = new Student("tom", 18);
          Student stu2 = new Student("jerry", 18);

          FileOutputStream fout = new FileOutputStream("stu.dat");
          ObjectOutputStream oout = new ObjectOutputStream(fout);
          oout.writeObject(stu1);
          oout.writeObject(stu2);

          oout.close();

          FileInputStream fin = new FileInputStream("stu.dat");
          ObjectInputStream oin = new ObjectInputStream(fin);
          Student s1 = (Student) oin.readObject();
          Student s2 = (Student) oin.readObject();

          oin.close();
          System.out.println(s1.name + " " + s1.age);
          System.out.println(s2.name + " " + s2.age);
     }
}

Reader,FileReader,FileWriter
Reader<-InputStreamReader,Reader<-OutputStreamWriter
通过桥转换获得字符流,也是一个获得字符流的方式。这种方式有两个用法:
1 、如果需要指定编码方式,则应当使用桥转换。
2 、在无法直接获得字符流的情况下,可以先获得字节流,再通过桥转换获得字符流。
利用桥转换进行编程,需要以下五个步骤:
1 、创建节点流
2 、桥转换为字符流
3 、在字符流的基础上封装过滤流
4 、读 / 写数据
5 、关闭外层流
BufferedReader, PrintWriter
import java.io.*;
public class TestPoem {
     public static void main(String[] args) throws Exception {
          // 创建节点流
          FileInputStream fin = new FileInputStream("poem.txt");
          // 桥转换
          Reader r = new InputStreamReader(fin, "GBK");
          // 封装过滤流
          BufferedReader br = new BufferedReader(r);
          // / 写数据
          String line = null;
          while( (line=br.readLine()) !=null){
               System.out.println(line);
          }

          // 关闭外层流
          br.close();
     }
}


import java.io.*;
public class TestPrintWriter {
     public static void main(String[] args) throws Exception {
          FileOutputStream fout = new FileOutputStream("poem2.txt");
          Writer w = new OutputStreamWriter(fout, "GBK");
          PrintWriter pw = new PrintWriter(w);
          pw.println(" 一个人在清华园 ");
          pw.println(" 我写的 Java 程序 ");
          pw.println(" 是全天下 ");
          pw.println(" 最面向对象的 ");
          pw.close();
     }
}

十.反射
*获取类对象
-类名.class,如Class c = int.class;
-getClass()方法, 通过类的对象获得类对象。
-Class.forName()方法,静态方法,签名如下,(已检查异常),会触发类加载
public static Class forName(String className) throws ClassNotFoundException
样例代码,
public static void main(String args[]) throws Exception{
     Class c = Class.forName("p1.p2.TestClass");
}

*使用类对象获取类的信息
getName(): 获得类的名称,包括包名;
getSimpleName(): 获得类的名称,不包括包名;
getSuperClass(): 获得本类的父类的类对象;
getInterfaces() : 获得本类所实现的所有接口的类对象,返回值类型为 Class[]。
import java.util.ArrayList;
public class TestClass1 {
     public static void main(String[] args) {
          Class c = ArrayList.class;
          String className = c.getName();
          System.out.println (" 类名: "+ className );
         
          String simpleName = c.getSimpleName();
          System.out.println (" 简单类名: "+ simpleName ) ;
         
          Class superClass = c.getSuperclass();
          System.out.println ( " 父类: "+ superClass .getName ()) ;

          Class[] interfaces = c.getInterfaces();
          System.out.println ( " 接口: ");
          for(int i =0 ; i < interfaces.length ; i++){
               System.out.println(interfaces[i].getName());
           }
     }
}

*使用类对象获取类中方法的信息
public Method[] getDeclaredMethods() throws SecurityException
public Method[] getMethods() throws SecurityException
getDeclaredMethods, 返回在本类中定义的所有方法,包括私有方法, 不能获得父类中的任何方法。
getMethods, 包括所有的公开方法,也包括父类中定义的公开方法, 私有方法不会被获取。
均抛出未检查异常,可处理可不处理。
样例代码,
public static void printMethod(Object obj){
     // 获取 obj 对象所对应的类对象
     Class c = obj.getClass();

     // 通过类对象,获取其中的所有方法对象
     Method[] ms = c.getMethods();

     // 打印每个方法的方法名
     for(int i = 0 ; i < ms.length ; i++){
          System.out.println(ms.getName());
     }
}

*使用类对象创建类的对象
public class Student {
     public String name;
     private int age;

     public Student(){
           System.out.println("Student()");
     }

     public String getName() {
          return name;
     }

     public void setName(String name) {
          this.name = name;
     }

     public int getAge() {
         return age;
     }

     public void setAge(int age) {
          this.age = age;
     }

     public void study(){
          System.out.println(name + " study");
     }

     public void study(int h){
         System.out.println(name + " study for " + h + " hours");
     }

     public double study(int a, double b){
          System.out.println(name + " study " + a + " " + b);
          return a * b;
     }

     private void play(){
         System.out.println(name + " play");
     }
}


import java.lang.reflect.*;
public class TestReflection {
     public static void main(String[] args) throws Exception {
         Class c = Student.class;
         Student stu = (Student) c.newInstance();
     }
}


public static Object createObject(String className){
     Object result = null;
     try{
          Class c = Class.forName(className);
          result = c.newInstance();
     }catch(Exception e){
          e.printStackTrace();
     }
     return result;
}

*Field类
Field getDeclaredField(String name)
Field getField(String name)
public void set(Object obj, Object value)
public Object get(Object obj)
public void setAccessible(boolean flag)
样例代码,
Student stu = new Student();
Class c = stu.getClass();
Field nameField = c.getField("name"); //1. 哪一个对象的属性
nameField.set(stu, "tom"); //2. 对象的哪一个属性,3 对象的属性值修改成什么


Class c = stu.getClass();
Field f = c.getDeclaredField("name");
Object value = f.get(stu);//要读取哪一对象的属性


Field f = stu.getClass().getDeclaredField("age");
f.setAccessible(true);
f.set(stu, new Integer(18) );//修改私有属性

*Method类
public Method getMethod(String name,Class[] parameterTypes)
public Method getDeclaredMethod(String name,Class[] parameterTypes)
getMethods, getDeclaredMethods类似,
第一个参数是一个字符串参数,表示的是方法的方法名。
第二个参数是方法的参数表, 无参方法,参数表就是一个空数组: new Class[]{}
Class c = Student.class;
Method m = c.getMethod("study", new Class[]{int.class,double.class});
样例代码(利用反射调用study方法),
Student stu = new Student();
Class c = stu.getClass();
//1 哪个对象调用方法
Method m = c.getDeclaredMethod("study", new Class[]{int.class, double.class}); 

//2,调用对象的哪个方法;
//3,传入实参;
//4,方法可以有返回值
Object result = m.invoke(stu, new Object[]{new Integer(10), new Double(1.5) } ); 
其中, public Object invoke(Object obj, Object[] args)
1 、第一个参数 obj 表示对哪一个对象调用方法
2 、第二个参数表示调用方法时的参数表
3 invoke 方法的返回值对应于 Method 对象所代表的方法的返回值。
4 、调用私有方法前,先调用 setAccessible(true)。

*Constructor类
getConstructors/ getDeclaredConstructors
1,可以通过 Class 类中的 getConstructors() / getDeclaredConstructors() 获得 Constructor 数组。
2,可以通过 Class 类中的 getConstructor() / getDeclaredConstructor() 来获得指定的构 造方法。 这两个方法只有一个参数:一个 Class 数组。
3,可以调用 Constructor 类中的 newInstance 方法创建对象。创建对象的时候,会调用相应的构造方法。
注: 如果创建对象只需要调用无参构造方法的话,就可以直接使用 Class 类中的 newInstance 方法,如果在创建对象的时候需要指定调用其他构造方法的话,就需要使用 Constructor 类。
样例代码,
import java.lang.reflect.*;
class Dog{
     String name;
     int age;

     public Dog(){
          System.out.println("Dog()");
     }

     public Dog(String name, int age) {
          System.out.println("Dog(String, int)");
          this.name = name;
          this.age = age;
     }
     
     public String toString(){
          return name + " " + age;
     }
}

public class TestConstructor {
     public static void main(String[] args) throws Exception {
          Class c = Dog.class;
                                              
                        //无参构造方法
          Dog d1 = (Dog) c.newInstance();
          System.out.println(d1);

          
                        // 获得构造方法
          Constructor con = c.getConstructor( new Class[]{String.class, int.class});

          // 创建对象时指定构造方法
                      // 为构造方法传递的参数
          Dog d2 = (Dog) con.newInstance( new Object[]{"Snoopy", new Integer(5)});
          System.out.println(d2);
     }
}

*反射的作用
创建 Student 对象,并调用 study() 方法的代码。
String className = "Student";
Class c = Class.forName(className);
Object o = c.newInstance();

String methodName = "study";
Method m = c.getMethod(methodName , new Class[]{});
m.invoke(o , new Object[]{});

反射技术有着非常显著的几个缺点。
1,运行效率与不用反射的代码相比会有明显下降。
2,代码的复杂度大幅提升,这个从代码量上大家就能比较出来
3,代码会变得脆弱,不易调试。使用反射,我们就在一定程度上绕开了编译器的语法 检查,例如,用反射去调用一个对象的方法,而该对象没有这个方法,那么在编译时,编译器是无法发现的,只能到运行时由JVM抛出异常。


十一.OOAD初步
*依赖、聚合与组合
java面向对象编程基础-读书笔记
依赖关系,又被称为 use-a 关系。 A 类中有一个 B 类型的局部变量(当然,方法参数也可以认为是特殊的局部变量)。
class Person{
     public void crossRiver(Boat b){
          ...     
     }
}
java面向对象编程基础-读书笔记java面向对象编程基础-读书笔记

关联关系,指的是 A 类中有一个 B 类型的属性。
人“有”自行车,在这种关系中,人和自行车是相对比较独立的对象。这个独立,指的 是外部对象“人”和内部对象“自行车”的生命周期之间,没有必然的联系, 称之为“聚合”。
外部对象一旦不存在,则内部对象也一定不存在了,即外部对象管理内部对象的生命周期, 称之为“组合”。

关联关系的方向性与多重性
java面向对象编程基础-读书笔记
java面向对象编程基础-读书笔记
java面向对象编程基础-读书笔记
java面向对象编程基础-读书笔记

*单例模式
public class Singleton{
     private Singleton(){}
     private static Singleton instance = new Singleton();
     public static Singleton getInstance(){
          return instance;
     }
}
写单例模式的时候,有三个要点:
1 、私有的构造方法;
2 、 静态的 instance 属性;
3 、静态的 getInstance() 方法。

*设计原则
开闭原则: 对扩展开放,对修改关闭。
实现开闭原则,软件应该具备以下特点:
可重用性
可扩展性
弱耦合性
各司其职

*三层体系结构:数据访问层、业务逻辑层、数据显示层
//构造接口
//Dao.java
package dao;
public interface Dao {
     String getData();
}

//Biz.java
package biz;
import dao.Dao;
public interface Biz {
     void setDao(Dao dao);
     String dealData();
}

//View.java
package view;
import biz.Biz;
public interface View {
     void setBiz(Biz biz);
     void showData();
}
 
//实现接口
//FileDaoImpl.java
package dao;
import java.io.*;
public class FileDaoImpl implements Dao {//数据访问层
     public String getData() {
          String data = null;
          BufferedReader br = null;
          try{
               br = new BufferedReader(new FileReader("test.txt"));
               data = br.readLine();
          }catch(IOException e){
               e.printStackTrace();
          }
          finally{
               if (br != null){
                    try
                    {
                         br.close();
                    }catch(IOException e){
                         e.printStackTrace();
                    }
               }
          }
          return data;
     }
}

//UpperCaseImpl.java
package biz;
import dao.Dao;
public class UpperCaseBizImpl implements Biz {//业务处理层,实现转大写
     private Dao dao;
     public String dealData() {
          String data = dao.getData();
          if (data != null){
               data = data.toUpperCase();
          }
          return data;
     }

     public void setDao(Dao dao) {
          this.dao = dao;
     }
}

//TextViewImpl.java
package view;
import biz.Biz;
public class TextViewImpl implements View {//数据显示层
     private Biz biz;
     public void setBiz(Biz biz) {
          this.biz = biz;
     }

     public void showData() {
          String data = biz.dealData();
          System.out.println(data);
     }
}

//简单工厂模式应用+配置文件+反射,SimpleFactory.java
package factory;
import dao.*;
import biz.*;
import view.*;
import java.util.Properties;
import java.io.*;

public class SimpleFactory {//工厂模式,对象创建过程独立
     private Properties props;

     public SimpleFactory(){
          props = new Properties();
          InputStream is = null;
          try{
               is = new FileInputStream("conf.props");
               props.load(is);
          }catch(IOException e){
               e.printStackTrace();
          }

          finally{
                   if (is!=null){
                        try {
                              is.close();
                        } catch(Exception e){
                             e.printStackTrace();
                        }
                    }
         }
     }

     public Dao createDao(){
          String className = props.getProperty("dao");
          Dao dao = (Dao) createObject(className);
          return dao;
     }

     public Biz createBiz(){
          String className = props.getProperty("biz");
          Biz biz = (Biz) createObject(className);
          return biz;
     }

     public View createView(){
          String className = props.getProperty("view");
          View view = (View) createObject(className);
          return view;
     }

     private Object createObject(String name){
          Object result = null;
          try {
               Class c = Class.forName(name);
               result = c.newInstance();
          } catch (Exception e) {
               e.printStackTrace();
          }
          return result;
     }
}

//主程序,Test.java
package test;

import dao.*;
import biz.*;
import view.*;
import factory.SimpleFactory;

public class Test {
     public static void main(String[] args) {
          SimpleFactory factory = new SimpleFactory();
          Dao dao = factory.createDao();
          Biz biz = factory.createBiz();
          biz.setDao(dao);

          View view = factory.createView();
          view.setBiz(biz);

          view.showData();
     }
}
其中,配置文件conf.props内容格式如下,
view=view.TextViewImpl
biz=biz.UpperCaseBizImpl
dao=dao.FileDaoImpl
Java 中,有一个非常方便的类: java.util.Properties Hashtable 的子类,它是一个特殊的 Map。这个类的 getProperty 方法,接受字符串类型的参数,表示“键”;返回值也是字符串,对应的是值。
这个类的 load 方法,读入配置文件, 可以接受一个 InputStream 参数。如果要读文件的话,可以创建一个 FileInputStream 作为 load 方法参数, load 方法会自动解析输入流,如 conf.props文件 ,会被自动解析出三个键值对放入 Properies 中,键分别为“ view ”、“ biz ”、“ dao ”,对应的值为“ view.TextViewImpl ”、 “ biz.UpperCaseBizImpl ”、“ dao.FileD aoImpl ”。

当有新需求,希望把所有字符改成小写,只需要修改配置文件:
biz=biz.LowerCaseBizImpl
//新的实现类如下,
package biz;
import dao.Dao;
public class LowerCaseBizImpl implements Biz {
     private Dao dao;
     public String dealData() {
          String data = dao.getData();
          if (data != null){
               data = data.toLowerCase();
          }

          return data;
     }

     public void setDao(Dao dao) {
          this.dao = dao;
     }
}

整个应用的类图如下,
java面向对象编程基础-读书笔记
编译与运行
javac -d . Dao.java FileDaoImpl.java
javac -d . Biz.java UpperCaseBizImpl.java
javac -d . View.java TextViewImpl.java
javac -d . SimpleFactory.java
javac -d . Test.java
java test.Test