//第三讲 //面向对象(上) /* 理解面向对象的概念 面向过程 在一个结构体中定义窗体的大小,位置,颜色,背景等属性,对窗口操作的函数窗口本身的定义没有任何关系 如HideWindow, MoveWindow,MinimizeWindow.这些函数都需要接受一个代表要被操作的窗口参数,是一种谓语与宾语的关系 面向对象 定义窗口,除了要指定在面向过程中规定的那些属性,如大小,位置,颜色,背景等外,还要指定该窗口可能具有的动作 如隐藏,移动,最小化等,这些函数被调用时,都是以某个窗口要隐藏,某个窗口要移动的语法格式来调用,这是一种主语与谓语的关系 面各向对象的三大特征 封装:(Encapsulation) 继承:(Inheritance) 多态:(Polymorphism) 类与对象 类是对象一类事物的描述,是抽象的,概念上定义;对象是实际存在的该类事物的每个个体,因而也称实例(instance) 如果将对象比作汽车,那么类就是气车的设计图纸,所以面向对象程序设计的重点是类的设计,而不是对象的设计 类的定义 class Person { int age; void shout() { System.out.println("oh, my god! I am"+age); } } age是类的属性,也叫类成员变量 shout是方法也叫类的成员函数 shout方法可以直接访问同一个类中的age变量,如果一个方法中有与成员变量同名的局部变量,该方法中对这个变量名的访问是局部变量,而不是成员变量 对象的产生 Person p1 = new Person();执行完后的内存状态 当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值 除了基本数据类型之外的都是变量类型都是引用类型,如上面的Person及前面讲过的数组 对象的使用 创建新的对象之后,我们就可以使用,对象名。对象成员的格式,不访问对象的成员 对象的生命周期 对象的比较 "=="运算符与equals()方法的区别 ==运算符是比较两个类的地址 equals是不用比较两个类的内容 怎样比较两个数组对象的内容是否相等 System.arraycopy() arrays.sort() 匿名对象 我们也可以不定义对象的句柄,而直接调用这个对象的方法,这样的对象叫做匿乐对象 如: new Person().shout(); 如果一个对象只需要进行一次方法调用,那么就可以使用匿乐对象 我们经常将匿名对象作为实参传递给一个函数调用 实现类的封装性 1 如果外面的程序可以随意修改一个类成员变量,会造成不可预料的程序错误,就象一个人的身高, 不能被外部随意的修改,只能通过各种摄取营养的方法去修改这个属性 2 在定义一个类成员(包括变量和方法)时,使用private关键字说明这个成员的访问权限,这个成员成了类的私有成员,只能被这个类的其它成员方法调用 而不能被其它类中的方法调用 3 为了实现良好的封装性,我们通常将类的成员变量声明为private,再通过public的方法来对这个变量进行访问 对一个变量的操作,一般都是读取和赋值操作,我们分别定义两种方法来实现这两种操作 一个是getXxx(),用来读取成员变量的操作值 另一个是setXxx()用来设置成员变量的赋值 4 一个类通常就是一个小的模块,我们应该让模块仅仅公开必须要让外界知道的内容,而隐藏其它的一切内容 我们在进行程序的详细设计时,应该尽量避免一个模块直接修改或操作另一个模块的数据,模块设计追求强内聚(许多功能尽量在类的内部独立完成,不让外面干预) 弱耦合(提供给外部尽量少的方法调用) 用总统指挥一支军队的例子来说明这种效果 类的封装所带来的优点 隐藏类的实现细节 让使用者只能通过事先定制好的方法来访问数据,可以方便的加入控制逻辑,限制对属性的不合理操作 便于修改,增强代码的可维护性 构造函数的定义与作用 构造方法的特征 它具有与类相同的名称 它不含返回值 它不能在方法中用return语句返回一个值 注意: 在构造方法里不含返回值的概念是不同于void的,在定义构造函数时加了void, 结果这个方法就不再被自动调用了 构造方法的作用: 当一个类的实例对象刚产生时,这个类的构造方法就会自动被调用,我们可以在这个方法中加入要完成初始化工作的的代码 这就好像我们规定每个人一出生就必须先洗澡,我们就可以在人的构造方法中加入完成洗澡的程序代码,于是每个人一出生就会自动完成洗澡,程序就不必再在每个人刚出生时一个一个地告诉他们要洗澡了 构造方法的重载 和一般的方法重载一样,重载的构造方法具有不同个数不同类型的参数,编译器就可以根据这一点判断出用new关键字产生对象时 该调用哪里个构造方法了,产生对象的格式是: new 类名(参数列表) 重载构造方法可以完成不同初始化的操作,如:p3 = new Person("Tom", 19); 语句,会做这样几件事 创建指定类的新实例对象 在堆内存中为例实对象分配内存空间,并调用指定类的构造方法 最后将实例对象的首地址赋值给引用变量p3 this是什么? 1 如果func2方被调用,一定是事先已经有了一个存在的对象 func2被作为那个对象的方法被使用 2 func2内部能引用的对象,同样也能引用func2所属的那个对象 3 在func2中,自己所属的那个对象的引用名称是什么呢?this关键字在java程序里的作用和它的词义很接近 它在函数内部就是这个函数所属的对象的引用变量 this引用句柄的存放位置 每个成员方法内部,都有一个this引用变量,指向调用这个方法的对象,类中的成员方法与this之间的关系如图 this引用句柄的应用 一个类中的成员方法可以直接调用同类中的其他成员,其实我们在一个方法内部使用this.其它成员 的引用方式和直接使用其它成员的效果是一样的,那this还有多大的作用呢?在有些情况下,我们还是非得用this关键字不可的: 让类的成员变量名和对其进行赋值的成员方法的形参变量同名是必要的,这样的代码谁看了都能明白这两个变量是彼此相关的 老手看到函数的定义,就能揣摩出函数中的代码,大大节省了另人和自己日后阅读程序的时间 假设我们有一个容器类和一个部件类,在容器类的某个方法中要创建部件类的实例对象,而部件类的构造方要接收一个代表其所有容器的参数 构造方法是在产生对象时被java系统自动调用的,我们不能在程序中像调用其它方法一样调用构造函数,但我们可以在一个构造方法里调用重载的构造方法,不是用构造方法名,而是用this(参数列表)的形式,根据其中的参数列表,选择相应的构造方法 垃圾回收过程分析 1 C++中的析构方法 2 java中的finalize()方法 3 System.gc的作用 */ class Lesson3 { public static void main(String[] args) { /*Person p1 = new Person(); Person p2 = new Person(); p1.setAge(22); //p1.age = 22; p1.shout(); p2.setAge(44); //p2.age = 44; p2.shout(); p1.getSomeOne(p2); //p1.getSomeOne(p2);*/ /*String str1 = new String("abc"); String str2 = new String("abc"); String str3 = str1; if(str1 == str2) { System.out.println("str1等于str2 true"); }else{ System.out.println("str1不等于str2 false"); } if(str1 == str3) { System.out.println("str1等于str3"); }else{ System.out.println("str1不等于str3"); } if(str1.equals(str2)) { System.out.println("str1等于str2 equals"); }*/ /*Person p1 = new Person("xlc", 22); p1.shout(); Person p2 = new Person(); p2.shout(); Person p3 = new Person("fangjin"); p3.shout();*/ //Person p1 = new Person("xlc",22); //p1.fun2(); new Person(); new Person(); new Person(); System.gc(); } }; class Person { private int age; private String name; //public void Person() //加上了void后构造函数将不会被自动调用 public Person() { this.age = 10; System.out.println("the constructorl is calling"); } /**/ public Person(String name) { this.name = name; } public Person(String name, int age) { //this.name = name; //一个构造方法,调用另一个构造函数 this(name); this.age = age; } void shout() { //int age = 60; System.out.println("my name is :"+this.name); System.out.println("my age is :"+this.age); } public void setAge(int x) { if(age < 0){ return; } this.age = x; } public int getAge() { return this.age; } public void getSomeOne(Person p) { p.shout(); } public void fun2() { Person a2 = new Person("love you"); a2.fun1(); } public void fun1() { System.out.println("name:"+this.name); } public void finalize() { System.out.println("ojbect is going"); } }; class Container { Component comp; public void addComponent(Component comp) { //this.comp = comp; this.comp = new Component(this); } }; class Component { Container c; public Component(Container c) { this.c = c; } };
/* 函数的参数传递 基本数据类型的参数传递 -> 例如普通的局部变量 函数的参数传递 --引用数据类型的参数传递 -> 对像为参数传递的情况 -> 数组的传递,这种传递相当于php中的引用传递 static 静态方法 1 在静态方法里只能调用同类中其它的静态成员(包括变量和方法), 而不能直接访问类中的非静态成员,这是因为,对于非静态的方法和变量,需要先创建类的实例对象才能使用 而静态方法在使用前不用创建任何对象 2 静态方法不能以任何方式引用this和super关键字(super关键字在下一章讲解) 与上面的道理一样,因为静态方法在使用前不用创建任何实例对象,当静态方法被调用时 this所引用的对象根本就没有产生 3 main()方法是静态的,因此JVM在执行main方法时不创建main方法所在的类的实例对象, 因为在Main方法中,我们不能访问该类中的非静态成员,必须创建该类的一个实例对象后, 才能通过这个对象去访问类中的非静态成员,这种情况,我们在以后的例子中会多次碰到 static静态变量 当我们编写一个类时,其实就是描述其对象的属性和行为,而并没有产生实质上的对象 只有通过new关键字才会产生出对象, 这时系统会分配内存空间给对象,其方法才可以供外部所调用 我们有时候希望无论是否产生了对象或无论产生了多少个对象的情况下 某些特定的数据在内存空间里只有一份,例如所有的中国人都有一个国家名称 所有中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量 编写使用静态变量统计一个类产生的实例对象的个数的程序 静态代码块 1 一个类中可以使用不包含在任何方法体中的静态代码块(static block) 当类被载入时,静态代码块被执行,且只被执行一次,静态块经常用来进行类属性的初始化 2 类中的静态代码块被自动执行,尽管我们产生了类的多个实例对象,但其中的静态代码块只被执行了一次, 当一个程序中用到了其他的类,类是在第一次被使用的时候才被装载,而不是在程序启动时就装载程序中所有可能要用到的类 单态设计模式 设计模式是在大量的实践中总结和理论化之后优选的代码结构,编程的风格,以及解决问题的思考方法 设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免得我们再去思考和摸索 失败为成功之母,但是要以大量的时间和精力为代价,如果成功经验可以借鉴,没有人再愿意去甘冒失败的风险, 我们没有理由不去了解和掌握设计模式,这也是java开发者提高自身素质的一个很好的选择 使用设计模式也许会制约你去创新,不过真正有意义的创新只能出自天才 即使你是天才,虽不必因循守旧,但也不可能完全不去借鉴前人的成的经验 所谓类的单态设计模式,就是采取一定的方法保存在整个软件系统中,对某个类只能存在一个对象的实例 并且该类只提供一个取得其对象实例的方法,如果我要让类在一个虚拟机中只能产生一个对象, 我们首先必须将类的构造方法的访问权限设置为private,这样 这样就不能用new操作符在类的外部产生类的对象 但在类的内部仍可以产生该类的对象,因为在类的外部开始还无法得到类的对象,只能调用该类某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以 指向类的内部产生的该类对象的变量也必须定义成静态的 理解mina方法的语法 由于java虚拟机需要调用类的mina()方法,所以该方法的访问权限必须是public 又因为java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的 该方法接收一个String类型的数组参数,该数组中保存执行java命令的传递给所运行的类的参数 */ class PassParam { int x; public static void main(String[] args) { //int x = 5; //change(x); //System.out.println(x); /*PassParam obj = new PassParam(); obj.x = 5; change(obj); System.out.println(obj.x);*/ /*int[] ar = new int[1]; ar[0] = 5; change(ar); System.out.println(ar[0]);*/ //new Count(); //new Count(); //new Count(); //Count.getCount(); } /* public static void change(int x) { x = 3; }*/ /*public static void change(PassParam obj) { obj.x = 3; }*/ public static void change(int[] x) { x[0] = 3; } } class Count { public static int count; public Count() { Count.count++; } public static void getCount() { System.out.println("count:"+Count.count); } }; class Chinese { //static String country = "中国"; //private static int count = 0; //private static String country = "中国"; /*static { count = 2; System.out.println("static code :"+count); } String name; int age; void singOurCountry() { System.out.println(country); }*/ private Chinese() { } private String name; private static Chinese obj; public static Chinese init() { if(Chinese.obj == null) { Chinese.obj = new Chinese(); } return Chinese.obj; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void show() { System.out.println("hello "+this.name); } }; class TestChinese { public static void main(String[] args) { //System.out.println(Chinese.country); //Chinese ch1 = new Chinese(); //ch1.singOurCountry(); System.out.println(args[0]); System.out.println(args[1]); Chinese c1 = Chinese.init(); c1.setName("xlc"); c1.show(); Chinese c2 = Chinese.init(); c2.show(); } };
/* 内部类 在类中直接定义的内部类 嵌套类可以直接访问嵌套它的类的成员,包括private成员,但是, 嵌套类的成员都不能被嵌套它的类直接访问 在内部类对象保存了一个对外部类对象的引用,当内部类的成员访法中访问某一变量时 如果在该方法和内部类中都没有定义过这个变量,内部类中对this的引用会被传递给那个外部类对象的引用, 如果是static修饰一个内部类,这个类就相当于一个外部定义的类,所以static的内部类中可以声明static成员,但是,非static的内部类中的成员是不能声明为static的 static的内部类不能再使用外层封装的非static的成员变量,这个道理不难想象,所以static嵌套类很少使用 外部类的方法是不能访问内部类的变量的 如果函数的局部变量(函数的形参也是局部变量)内部类的成员变量 外部类的成员变量名,我们应该按下面的程序代码所使用的方法来明确指定我们真正要访问的变量 在方法中定义内部类 嵌套类并非只能在类中定义,也可以在几个程序块的范围之内定义内部类, 例如,在方法中,或甚至在for循环体内部,都可以定义嵌套类 在方法中定义的内部类只能访问方法中的final类型的局部变量 用final定义的局部变量相当于是一个常量,它的生命周期超出方法运行的生命周期 public class Outer { private int size; public class Inner { private int size; public void doStuff(int size) { size ++; //引用的是doStuff()函数的形参 this.size ++; //引用的是Inner类的变量 Outer.this.size ++; //是指外部类的变量 } } } */ /*class Outer { int outer_i = 100; void test() { Inner in = new Inner(); in.display(); inner_i = 30; //外部类不能直接访问内部类的变量 } public static void main(String[] args) { Outer outer = new Outer(); outer.test(); } //static class Inner //相当于该类在外部定义了 calss Inner { int inner_id = 50; void display() { System.out.println("outer_i = "+outer_i); } }; };*/ /*class Outer { private int size = 10; class Inner { public void display() { System.out.println(++Outer.this.size); } }; }; class test { public static void main(String[] args) { System.out.println("test"); Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); //这里的new有点意思哈 inner.display(); } };*/ class Outer { int outer_i = 100; void test() { final int x=1; //int x=1; class Inner { public void display() { System.out.println("x:"+x); }; }; Inner in = new Inner(); in.display(); //inner_i = 30; //外部类不能直接访问内部类的变量 } public static void main(String[] args) { Outer outer = new Outer(); outer.test(); } };
import java.io.*; /** * Title: engineer类<br> * Description: 通过engineer类来说明java中的文档注释<br> * Copyright: (c) 2013 www.xlc.com<br> * Company: 北京铂原有限公司<br> * @author 向铃川 * @version 1.00 */ public class engineer { public String Engineer_name; /** * 这是engineer对象的构造函数 * @param name engineer的名字 */ public engineer(String name) { } /** * 这是repairing方法的说明 * @param sum 需要修理的机器总数 * @param alltime 需要修理的总时间 * @return Repqiring的数量 */ public int repairing(int sum, int alltime) { } } //进入目录,执行javadoc -d engineer -author -version engineer.java