黑马程序员——面向对象(一)---面向对象、函数的参数传递、static、内部类、文档注释

来源:互联网 发布:检测僵尸粉软件 编辑:程序博客网 时间:2024/05/30 22:44

-------  android培训、java培训、期待与您交流! ----------


第一部分 面向对象

一.理解面向对象的概念

      1.面向过程:

         在一个结构体中定义窗口的大小,位置,颜色,背景等属性,对窗口操作的函数与窗口本身的定义没有任何关系,如HideWindowMoveWindowMinimizeWindow,这些函数都需要接受一个代表要被操作的窗口参数 ,是一种谓语与宾语的关系 。

     2.面向对象

        定义窗口时,除了要指定在面向过程中规定的那些属性,如大小,位置,颜色,背景等外,还要指定该窗口可能具有的动作 ,如隐藏,移动,最小化等。这些函数被调用时,都是以某个窗口要隐藏,某个窗口要移动的语法格式来使用的 ,这是一种主语与谓语的关系。

     3.面向对象的三大特征

              1>封装  (Encapsulation)

              2>继承  (Inheritance)

             3>多态  (Polymorphism)

将一个窗口的属性和所有可能要执行的动作放在一起定义,这就是面向对象的封装性。

二.类与对象

类是对某一类事物的描述,是抽象的、概念上的定义;

对象是实际存在的该类事物的每个个体,因而也称实例(instance)

如果将对象比作汽车,那么类就是汽车的设计图纸。所以面向对象程序设计的重点是类的设计,而不是对象的设计。

1.类的定义

class Person {int age;void shout() {System.out.println("oh,my god! I am" + age);}}

1>age是类的属性 ,也叫类成员变量 。

2>shout是方法也叫类的成员函数。类的行为。

3>shout方法可以直接访问同一个类中的age变量 ,如果一个方法中有与成员变量同名的局部变量,该方法中对这个变量名的访问是局部变量,而不再是成员变量。

2.对象的产生

Person p1 = new Person();执行完后的内存状态:

 

当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。除了基本数据类型之外的都是变量类型都是引用类型,如上面的Person及前面讲过的数组。 


注2:在一个方法中定义的内部变量时不会被初始化的,所以在变量使用之前要自己进行初始化赋值。

p1是对象的引用句柄,它是在栈中进行分配的,其值为对中对象的首地址。这样引用变量就指向了对象,以后就可以用引用变量代表堆中的对象。

3.对象的使用

创建新的对象之后,我们就可以使用“ 对象名.对象成员 ”的格式,来访问对象的成员(包括属性和方法) 

class TestPerson {

public static void main(String[] args) {

Person p1 = new Person();

Person p2 = new Person();

p1.age = -30;

p1.shout();

p2.shout();

}

}

上面程序运行的内存布局如下图 :

 

注3main函数虽然是在类中定义的,但它严格来说不属于类中的成员。因为Java程序启动运行时必须有一个main函数作为程序运行的入口,它放在哪一个类里面都无所谓,main函数所在的类被作为启动运行类,通过“java命令+启动运行类”的形式来进入程序执行入口。

4.对象的生命周期

 


 

 

 

 

5.对象的比较

1>==”运算符与equals()方法的区别

 

注1>”==”用于比较两个对象引用的值是否相等,即是否指向同一对象。

equals()方法用于比较两个引用所指向的对象中的内容是否相等。

2.怎样比较两个数组对象的内容是否相等?

2> System.arraycopy()    用于数组内容拷贝

Array.sort()   用于数组排序

Array.equals() 用于比较两个数组是否相等

6.匿名对象

1>我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象, 如:new Person().shout(); 

2>如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。 

3>我们经常将匿名对象作为实参传递给一个函数调用。

三.类的封装性

1.实现类的封装性

1>如果外面的程序可以随意修改一个类的成员变量,会造成不可预料的程序错误,就象一个人的身高,不能被外部随意修改,只能通过各种摄取营养的方法去修改这个属性。 

2>在定义一个类的成员(包括变量和方法)时,使用private关键字说明这个成员的访问权限,这个成员成了类的私有成员,只能被这个类的其他成员方法调用,而不能被其他的类中的方法所调用。 

3>为了实现良好的封装性,我们通常将类的成员变量声明为private,再通过public的方法来对这个变量进行访问。对一个变量的操作,一般都有读取和赋值操作,我们分别定义两个方法来实现这两种操作,一个是getXxx()Xxx表示要访问的成员变量的名字),用来读取这个成员变量操作,另外一个是setXxx()用来对这个成员变量赋值。

4>一个类通常就是一个小的模块,我们应该让模块仅仅公开必须要让外界知道的内容,而隐藏其它一切内容。我们在进行程序的详细设计时,应尽量避免一个模块直接修改或操作另一个模块的数据,模块设计追求强内聚(许多功能尽量在类的内部独立完成,不让外面干预),弱耦合(提供给外部尽量少的方法调用)。用总统指挥一支军队的例子来说明这种效果。

2.类的封装所带来的优点、

1>隐藏类的实现细节;

2>让使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;

3>便于修改,增强代码的可维护性;

四.构造函数的定义与作用

1.构造方法的特征

1>它具有与类相同的名称;

2>它不含返回值;

3>它不能在方法中用return语句返回一个值

1:在构造方法里不含返回值的概念是不同于“void”的,在定义构造方法时加了“void”,结果这个方法就不再被自动调用了。

2.构造方法的作用

构造方法的作用:当一个类的实例对象刚产生时,这个类的构造方法就会被自动调用,我们可以在这个方法中加入要完成初始化工作的代码。这就好像我们规定每个“人”一出生就必须先洗澡,我们就可以在“人”的构造方法中加入完成“洗澡”的程序代码,于是每个“人”一出生就会自动完成“洗澡”,程序就不必再在每个人刚出生时一个一个地告诉他们要“洗澡”了。

3.构造方法的重载

1>和一般的方法重载一样,重载的构造方法具有不同个数或不同类型的参数,编译器就可以根据这一点判断出用new 关键字产生对象时,该调用哪个构造方法了。产生对象的格式是:new 类名(参数列表)  

2>重载构造方法可以完成不同初始化的操作, 如:p3=new Person(Tom,18);语句,会做这样几件事:创建指定类的新实例对象,在堆内存中为实例对象分配内存空间,并调用指定类的构造方法,最后将实例对象的首地址赋值给引用变量p3

4.构造方法的一些细节

1>java每个类里都至少有一个构造方法,如果程序员没有在一个类里定义构造方法,系统会自动为这个类产生一个默认的构造方法,这个默认构造方法没有参数,在其方法体中也没有任何代码,即什么也不做。 

2>由于系统提供的默认构造方法往往不能满足编程者的需求,我们可以自己定义类的构造方法,来满足我们的需要,一旦编程者为该类定义了构造方法,系统就不再提供默认的构造方法了。 

3>声明构造方法,如无特殊需要,应使用public关键字,在我们前面例子中,可以使用private访问修饰符吗?答:可以,不想让类创建对象时。 

五.this是什么?

 

1.如果func2方法被调用 ,一定是事先已经有了一个存在的对象,func2被作为那个对象的方法被使用。 

2.func2内部能引用别的对象 ,同样也能引用func2所属的那个对象。

3.func2中,自己所属的那个对象的引用名称是什么呢? this关键字在java程序里的作用和它的词义很接近,它在函数内部就是这个函数所属的对象的引用变量。

4.this引用句柄的存放位置

每个成员方法内部,都有一个this引用变量,指向调用这个方法的对象,类中的成员方法与this之间的关系如图:

 

5.this引用句柄的应用

非得用this关键字的情况:

1>让类的成员变量名和对其进行赋值的成员方法的形参变量同名是必要的,这样的代码谁看了都能明白这两个变量是彼此相关的,老手看到函数的定义,就能揣摩出函数中的代码,大大节省了别人和自己日后阅读程序的时间。 

2>假设我们有一个容器类和一个部件类,在容器类的某个方法中要创建部件类的实例对象,而部件类的构造方法要接收一个代表其所在容器的参数。

class Container{Component comp;public void addComponent(){//把方法所在容器对象传给新创建的组件对象comp=new Component(this);}}class Component{Container cont;public Component(Container cont) {this.cont=cont;}}

3>构造方法是在产生对象时被java系统自动调用的,我们不能在程序中象调用其他方法一样去调用构造方法。但我们可以在一个构造方法里调用其他重载的构造方法,不是用构造方法名,而是用“this(参数列表)”的形式,根据其中的参数列表,选择相应的构造方法。 

class Student{private String name;private int age;public Student(){//构造方法使用this来引用其他的构造方法this("fda",25);}public Student(String name,int age){this.name=name;this.age=age;System.out.println("name="+this.name);System.out.println("age="+this.age);}}

六.垃圾回收过程分析

1.c++中的析构方法 

在对象被从内存中释放时,析构方法就会被从自动调用,析构方法中的代码就得到了执行。

有了析构方法我们就可以实现每一个对象在被释放前完成一些清理工作方面的事情。

C++中用析构方法释放对象所占用的资源。

构造方法被调用时,对象已经被创建;析构方法被调用时,对象还没有被释放。

2.java中的finalize()方法 

Javafinalize方法与析构函数类似。但是,因为Java有垃圾回收机制,会自动释放对象存在期间所占用的资源,所以不用我们手动去释放了,这点与C++室友区别的。finalize方法不是在对象变为垃圾时被调用,而是在垃圾被垃圾回收器清除之前被调用。

3.System.gc的作用 

用来告诉垃圾回收器立即清除垃圾对象。

第二部分 函数的参数传递

一.基本数据类型的参数传递

 


 

 

二.引用数据类型的参数传递

 





三.引用参数传递的一个问题分析

若传入引用参数,但在函数内参数重新指向了一个新的对象,则虽进行了数据修改,但传入的对象不会被修改。

 



 

 

第三部分 static

一.静态变量

当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。

我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。在外部只是用类名就可引用静态成员变量。

 

编写使用静态变量统计一个类产生的实例对象的个数的程序:

class Student{public static int objcount=0;//每次创建对象调用构造方法时objcount变量加1public Student(){objcount++;}//当对象从内存中释放时objcount减1@Overrideprotected void finalize() throws Throwable {objcount--;<span style="white-space:pre"></span>}}

注一:访问静态成员变量的方式:

在类内: 直接使用不用加前缀  objcount

                        类名.静态变量名 Student.objcount

                          this.静态变量名 this.objcount

在类外:        类名.静态变量名 Student.objcount

                    对象名.静态变量名 stu.objcount

二.静态方法

1.在静态方法里只能直接调用同类中其它的静态成员(包括变量和方法),而不能直接访问类中的非静态成员。这是因为,对于非静态的方法和变量,需要先创建类的实例对象后才可使用,而静态方法在使用前不用创建任何对象。

2.静态方法不能以任何方式引用thissuper关键字(super关键字在下一章讲解)。与上面的道理一样,因为静态方法在使用前不用创建任何实例对象,当静态方法被调用时,this所引用的对象根本就没有产生。

3.main() 方法是静态的,因此JVM在执行main方法时不创建main方法所在的类的实例对象,因而在main()方法中,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在以后的例子中会多次碰到。

三.静态代码块

1.一个类中可以使用不包含在任何方法体中的静态代码块(static block ),当类被载入时,静态代码块被执行,且只被执行一次,静态块经常用来进行类属性的初始化。

2.类中的静态代码块被自动执行,尽管我们产生了类的多个实例对象,但其中的静态代码块只被执行了一次。当一个程序中用到了其他的类,类是在第一次被使用的时候才被装载,而不是在程序启动时就装载程序中所有可能要用到的类。

四.单例设计模式

1.设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就想是经典的棋谱,不同的棋局,我们用不同的棋谱,免得我们自己再去思考和摸索。失败为成功之母,但是要以大量的时间和精力为代价,如果有成功经验可借鉴,没有人再愿意去甘冒失败的风险,我们没有理由不去了解和掌握设计模式,这也是Java开发者提高自身素质的一个很好选择。使用设计模式也许会制约你去创新,不过真正有意义的创新只能出自少数天才,即使你就是那个天才,虽不必因循守旧,但也不可能完全不去了解和借鉴前人的成功经验。

2.所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new 操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。 

五.理解main方法的语法

由于java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数。 

 

第四部分 内部类

一.在类中直接定义的内部类

1.内部类可以直接外部类的成员,包括private成员,如同代码块的访问权限。

但是内部类的成员却不能外部类直接访问,外部类只能通过内部类的对象来访问。这如同两个平级的类之间的访问权限。

2.在内部对象中保存了一个对外部类对象的引用(Outer.this),当内部类的成员方法直接通过名称访问某一成员时,如果在该内部类和作用域内都没有定义过这个成员,内部类中对this的引用会被传递给那个外部类对象的引用。

 

注2:如果使用this.成员或Inner.this.成员则只能访问内部类中的成员。

Outer.this.成员,只能访问外部类成员。

3.如果用static修饰一个内部类,这个类就相当于是一个外部定义的类,所以static的内部类中可声明static成员。

但是,非static的内部类中的成员是不能声明为static的。

static的内部类不能再使用外层封装类的非static的成员变量,static嵌套类很少使用。

4.如果内部类函数的局部变量(函数的形参也是局部变量),内部类的成员变量,外部类的成员变量重名,我们应该按下面的程序代码所使用的方式来明确指定我们真正要访问的变量。 

class Outer{private int size;class Inner{private int size;public void doStuff(int size){size++;//引用的是doStuff函数的形参this.size++;//引用的是Inner类中的成员变量,也可用Inner.this.size++Outer.this.size++;//引用Outer类中的成员变量}}}

5.内部类如何被其他类引用 :

1>普通内部类

通过外部类对象创建内部类对象,再通过内部类对象访问内部类成员

public class InnerTest {public static void main(String[] args) {Outer outer=new Outer();//通过外部类的对象创建内部类的对象Outer.Inner inner=outer.new Inner();inner.doStuff();//访问内部类成员//或者new Outer().new Inner().doStuff();}}class Outer{private int size=10;class Inner{public void doStuff(){System.out.println(size++);}}}

2>静态内部类

其他类通过”外部类名.内部类名.成员”的方式访问静态内部类的静态成员

public class InnerTest {public static void main(String[] args) {Outer.Inner.doStuff();}}class Outer{static class Inner{public static void doStuff(){System.out.println("Outer.Inner.doStuff()");}}}

二.在方法中定义的内部类

1.内部类并非只能在类里定义,也可以在几个程序块的范围之内定义内部类。例如,在方法中,或甚至在for循环体内部,都可以定义内部类。

2.在方法中定义的内部类只能访问方法中的final类型的局部变量,用final定义的局部变量相当于是一个常量,它的生命周期超出方法运行的生命周期。

同样的,在方法中定义的内部类也可以访问外部类中的成员。

 

第五部分 使用Java的文档注释

一.示例:

/** * Title: engineer类<br> * Description: 通过engineer类来说明java中的文档注释<br> * Copyright: (c) 2014 www.itcast.cn<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 Repairing的数量 */public int repairing(int sum, int alltime) {return 0;}}

二.文档注释以“/**”开始,以“*/”标志结束,相应的信息和批注所对应的位置很重要! 类的说明应在类定义之前,方法的说明应在方法的定义之前。

三.批注参数来标记一些特殊的属性及其相应的说明 。

         @author<作者姓名>

         @version<版本信息>

         @param<参数名称><参数说明>

         @return<返回值说明>

0 0
原创粉丝点击