JavaSE学习笔记_3:Java面向对象

来源:互联网 发布:唢呐软件下载 编辑:程序博客网 时间:2024/05/16 18:29

Java面向对象

 

1、面向对象概念

面向对象的理解:

① 面向对象是相对面向过程而言的。

② 面向对象和面向过程都是一种思想。

③ 面向过程:强调的是功能行为。

④ 面向对象:强调的是具备了功能的对象。

⑤ 面向对象是基于面向过程。

 

(面试可能会问到)

将大象放进冰箱里。

我们知道,这件事情可以分三步走:把冰箱门打开,把大象放里边去,把冰箱门关上。注意,我们打开冰箱,存储大象,关上冰箱,我们在强调过程。你看,打开的动作啊,存储的动作啊,关闭的动作啊。恩,是这样的。存什么不重要,最重要的是把大象存进去了,大象指的是存储一个货物而已,一个物体而已。这个是一个标准的面向过程的思想。叫做我们注重的是一个过程,注重过程当中所涉及到的行为,也就是功能,即打开功能,存储功能,关闭功能。我们前几天写的代码都是在写过程,搞一个功能,再搞一个功能,接着又调用这个功能,我们玩的是过程。

这个过程强调的是那些行为、动作或者功能。那么把它转化成对象是什么样的呢?就是说不再面向这个过程了,而针对对象。那么对象又是什么呢?我们分析一下,可以发现,无论是打开,还是存储,还是关闭。它们是不是都是冰箱这类食物所具备的一个行为?对。因为你操作的都是冰箱,那么这个时候呢?就把这个思想转变了,叫什么呢?既然打开,存储和关闭都是冰箱的一个行为,是吧。我们只要操作这个冰箱中所具备的功能就可以的话,我们就把这些行为全都定义到冰箱当中,那么冰箱是不是实实在在的这么一个东西啊,对吧。这个我们就把这一个冰箱个体称之为一个对象。而我们现在再看到的东西就不再是打开,存储和关闭了。我们看到的就只有冰箱这么一个事物了,而冰箱具备打开,存储和关闭的功能。

 

用图来解释:

 

对于图中的第一幅图,我们把大象放进冰箱,我们的从头到尾执行这动作。而对于图中的第二幅图,我们只要有了冰箱,我们可以这样做:冰箱.打开、冰箱.存储、冰箱.关闭。强调的都是冰箱这个对象。所以左边叫做面向过程,右边叫做面向对象。

 

再说一个例子:

窗口:

可以打开、最大化、最小化、关闭等动作,那么我只要有了窗口,这些功能就全都在了。叫做窗口.打开、窗口.关闭等。强调窗口了。

面对好多个动作--->面向一个事物

(执行者) (指挥者)

 

说一下语言的对应:早期都听说过basic或者叫做VB也行。因为basic是一门语言,VB是一个可视化的basic。接下来,c是语言,这类语言都是标准的面向过程语言,ok。也就是说,c里边,大家只要定义那些功能就可以了,有了功能就可以运行了。后期过渡到面向对象,是怎么过渡的呢?发现这些功能都有其所属关系,对吧。每次我都要挨个去找这个功能好麻烦,我要找打开,有没有存储,我还要找存储。那么说我能不能把它封装一下,我把它们都存储到一个东西里边去,我找到这个东西,这些个功能就全在,更方便。那么,我们就先找到这些功能有哪些共性,到底所属于哪个事物,找到这个事物以后把这些功能都定义到这个事物里边去,这个事物拿到了,功能也就拿到了。(其实,要我说,面向对象就是提高代码复用性。一些功能我们只需要写一遍,即在定义类的时候,通过这个类可以new对象,有了对象,我们就有了功能,直接调用。Java中有很多已经定义好的类,我们可以直接拿来用的,还有很多是java没有定义的,需要我们自己去定义。     以前说的面向过程,通俗来说,就是我们没做一件事情都要一步一步的定义功能,写函数,也许这些函数所属一个事物,等到下次我们再碰到相同的问题完全可以拿来直接用,而面向对象并没有这个特性。还是一步一步写。)即变简单了。以前,我们针对这三个动作,如果你动作多,我们要针对,面向更多的动作,我要挨个去找这些动作并执行而现在我只需要面对一个就可以了。

 

面试中可能会问:

你是如何理解面向对象?

分两部分:

第一部分:说一下面向对象的特点:它是一个思想。它能让一个复杂的问题变得简单化。它能让我们角色从执行者转变为指挥者。别人怎么的我不知道,我只要结果。

 

第二部分:举个例子。最牛的例子就是结合实际场合来说。例:其实面试官你就是在用面向对象在思考问题的,公司里面现在有找人需求,,是因为用户量比较大,代表着公司也是蒸蒸日上在发展。你用面向对象在思考问题,你需要找一些专业编程经验的人来帮你完成公司的任务,那么我就是那个对象,我就是具备那个专业编程功能的对象,你找我就对了,其实你就是指挥我在做事情,你在使用我的专业编程功能为公司创造价值。当然,我不仅具备专业功能这个最基本的需求,我还具备表达能力,管理能力,你日后还能指挥我变成一个组长。这没有问题。呵呵。。。。

 

举个例子:

人开门:名词提炼法。

这里有两个对象:人和门,但是开这个动作,门最清楚。所以将该行为封装在门这个对象中。

{

开门(门)

{

门.开();

}

}

 

{

开(){操作门轴等。}

}

 

面向对象三个特征:封装、继承、多态。

 

以后开发其实就是在找对象使用。没有对象那个,就创建一个对象。找对象,使用对象。后期讲到继承和多态的时候还会有维护对象的关系。

 

2、类与对象的关系

先有的类?还是先有的对象?

相对于计算机语言而言,是先有的类才有的对象,是因为我们有描述才能产生对象。而万事万物都是对象,看到对象以后,我们才能去描述它。

 

类:对现实生活中事物的描述。

对象:这类事物,实实在在存在的个体。

 

生活中和计算机中的对应:

现实生活中的对象:张三、李四。

想要描述:提取对象中共性内容。对具体的抽象。抽象时,这些对象的共性有:姓名,年龄,性别,学习的功能。(虽不具体,但是这类事物确实是描述出来了。想要具体的话,就得落实奥每个个体身上,因为每个个体都有自己不同的姓名,性别和年龄,而且有具体学习的方法。)

 

映射到java中,描述就是class定义的类,具体的对象就是对应java在堆内存中用new建立的实体。

 

为什么说要放在堆内存中,还是个实体?

大家发现,这个对象里边包含了很多数据。一个张三,它里边包含了姓名,性别,n多个数据。我们说过凡是数据多了,我们需要用实体来存取,像数组一样,数组里边可以存储一些数据,对象也是用于存储数据的。那么,凡是用于存储多个数据的,我们都称之为实体,都放在堆内存中。

 

用图说话:

 

 

 

 

之前在数组中有讲过:不用的引用指向同一个对象,或者称之为实体。那么在面向对象中同样也存在,这里不在做过多的阐述。

 

总结:

用new来建立对象,把现实生活中的对象(独立的个体)放在堆内存中产生,那么对象的特点在于封装数据,数据包含着属性和行为,紧跟着想要操作对象中的那个内容就指挥对象做事情就可以了,指挥哪一个对象要明确一些,即对像点的形式就能完成。

 

发现:我们在描述事物的时候,这个类和我们以前写的稍微有点不一样。

第一个呢,发现没有主函数,那么我们都知道主函数的功能是为了保证一个类的独立运行,我们才去写主函数。那么当我们的类只为了描述事物,而且这个类不需要独立运行的时候是可以不写主函数的。简单来讲:我们可以把我们以前写的类叫做测试类,而现在写的类叫描述类。以后我们要完成一个任务,我们的一个java文件中可能会存在多个类,但是只有一个测试类。测试类中可以写一个主函数,整个文件中就只有这么一个主函数。这是唯一的一个入口。用于调用别人。我们在这个主函数中可以创建其他描述类对象,就可以使用描述类中的内容。

第二个不同在于变量定义的方式比较特别。以前都定义在函数里边现在定义在函数外边,叫做局部变量。记住,这个我们称之为成员变量,成员变量的特点:成员变量和局部变量不同在于它们的作用范围不一样,成员变量作用于整个类中,局部变量作用于函数中或语句中;还有在于在内存中的位置,成员变量在堆内存中,因为对象的存在才在内存中存在,而局部变量存在于栈内存中,因此成员变量在创建的时候不用初始化,而局部变量初始化需要;有初始化值。

拓展:

class Car

{

//描述颜色

String color=”红色”;

//描述轮胎数

int num=4;

//运行行为

void run()

{

System.out.println(color+”---”+num);

}

public static void main(String[] args)

{

Car c=new Car();

C.run();

}

}

Ps:这叫做在本类中创建本类对象。若在主函数中直接写上run();这是不可以的,语法上不允许,因为主函数是带一个static,要想直接访问得在run()上加static。一般情况下,我们不在里边写主函数,有很多类搞一个测试类里面写上主函数就行了,其他类在里边创建对象,调用类里边的东西。一个程序有N多个类组成,有一个入口就行了。

 

面向对象中访问规则1:

假设有一个程序:

(1)如果程序中只有一个类,有成员变量,有一般方法,有主函数。那么在主函数中想要访问成员变量和一般方法,这时候,如果成员变量(一般方法)是静态的,则直接访问;如果成员变量(一般方法)是非静态的,则必须在主函数中创建这个类的对象,用对象点的形式去访问。

(2)如果程序中有两个类,一个描述类,一个主类(测试类)。该描述类中有成员变量,有一般方法;该测试类中只有一个主函数。那么在测试类主函数中想要访问成员变量和一般方法,这时候,如果描述类中成员变量(一般方法)是静态的,则用类名点的形式去访问;如果描述类中成员变量(一般方法)是非静态的,则必须在测试类主函数中创建描述类的对象,用对象点的形式去访问。

(3)一个类中的非静态方法可以直接访问该类中的其他非静态方法和成员变量。

 

匿名对象:

① 匿名对象是对象的简化形式;

② 匿名对象两种使用情况:

当对象方法仅进行一次调用的时候;

 

     匿名对象可以作为实际参数进行传递。

 

演示一:

 

 

Car c=new Car();

c.run();

等价于:

new Car().run()

 

Car c=new Car();

c.run();

c.num=5;

不等价于:

new Car().run() 不是同一个对象

new Car().num=5

原因:前面一直是一个对象在调用,而后面是两个对象在调用。

 

注意:

Car c=new Car();

show(c);

show(new Car());

在内存中的区别。

 

 

下面语句对应的图可以类似去画,此处不多做阐述。

 

3、封装(面向对象的第一大特征)

封装:将使用者不需要,不能任意操作的东西都隐藏起来不让你访问,通过权限修饰符来实现(常用的权限修饰符是private)。仅对外提供公共访问方式。

 

 

好处:

① 将变化隔离。

② 便于使用

③ 提高重用性。

④ 提高安全性。

 

封装的情况:

(1)将函数封装。例如进制转化中trans()就可以封装起来。

(2)将属性封装。例如Person类中的成员变量age等就可以封装起来,防止被非法赋值。在提供的公共方法中可以控制传入的数据,判断是不是合法的或符合现实生活的数据。公共方法的名字是用来头的,分别是set、get来开头的。当封装的属性变多了,日后用高级编辑工具(如eclipse)的时候这些公共访问代码可以采用代码自动生成的方式。开发中习惯性动作。

 

示例:

class  Person

{

private int age;

public void setAge(int a)

{

if(a>0&&a<130)

{

age=a;

speak();

}

else

  System.out.println(”非法数据“);

}

public void getAge()

{

return age;

}

void speak()

{

System.out.println(“age=”+age);

}

 

}

class PersonDemo

{

public static void main(String[] args)

{

Person p=new Person();

p.setAge(-20);

p.setAge(30);

//p.speak();

}

}

运行结果:非法数据

age=30

 

4、构造函数

特点:

① 函数名与类名相同。

② 不用定义返回值类型。

③ 不可以写return语句。

作用:给对象进行初始化。对象一建立就会调用与之对应的构造函数(比如:可以发现new Person()与Person(){}中相同之处)。表示对象与生俱来的一些行为。

 

注意:

(1)默认构造函数的特点。

a. 当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造函数,默认构造函数的权限和所在类的权限一样。方便于该类进行初始化,否则对象是建立不出来的。

b. 当在类中定义了构造函数后,默认的构造函数就没有了。如果还想使用默认的构造的函数,必须自己手动的写出来。写出来就不叫做默认构造函数了,叫做自定义构造函数。

 

(2)多个构造函数是以重载的形式存在的。

比如,人这类事物所产生的对象,它们初始化的时候有可能不一样。有的孩子一出生就有姓名和年龄,有点孩子一出生只有年龄没有姓名等不同情况,所以我们就分别定义不同的构造函数对不同的对象进行初始化。如果将一个类中的所有构造函数私有化,代表着这个类是不可能创建对象。

 

示例:

class Person

{

private String name;

private int age;

Person()

{

System.out.println(“A:name=”+name+”,age=”+age);

}

Person(String n)

{

name=n;

System.out.println(“A:name=”+name+”,age=”+age);

}

Person(String n,int a)

{

name=n;

age=a;

System.out.println(“A:name=”+name+”,age=”+age);

}

}

class PersonDemo

{

public static void main(String[] args)

{

Person p1=new Person();

Person p2=new Person(“lisi”);

Person p3=new Person(“wangwu”,10);

}

}

运行结果:A:name=null,age=0

          B:name=lisi,age=0;

          C:name=wanwu,age=10

 

我们还可以构造函数数添加其他东西,表示对象与生俱来的多个特性。

class Person

{

private String name;

private int age;

Person()

{

System.out.println(“A:name=”+name+”,age=”+age);

System.out.println(”cry“);

}

Person(String n)

{

name=n;

System.out.println(“A:name=”+name+”,age=”+age);

System.out.println(”cry“);

}

Person(String n,int a)

{

name=n;

age=a;

System.out.println(“A:name=”+name+”,age=”+age);

System.out.println(”cry“);

}

}

class PersonDemo

{

public static void main(String[] args)

{

Person p1=new Person();

Person p2=new Person(“lisi”);

Person p3=new Person(“wangwu”,10);

}

}

 

 

运行结果:A:name=null,age=0

cry

          B:name=lisi,age=0;

cry

          C:name=wanwu,age=10

cry

 

为了提高代码复用性:可以将重复的代码单独封装成一个函数。

class Person

{

private String name;

private int age;

Person()

{

System.out.println(“A:name=”+name+”,age=”+age);

cry();

}

Person(String n)

{

name=n;

System.out.println(“A:name=”+name+”,age=”+age);

cry();

}

Person(String n,int a)

{

name=n;

age=a;

System.out.println(“A:name=”+name+”,age=”+age);

cry();

}

public void cry()

{

System.out.println(“cry.....”);

}

}

class PersonDemo

{

public static void main(String[] args)

{

Person p1=new Person();

Person p2=new Person(“lisi”);

Person p3=new Person(“wangwu”,10);

}

}

我们再看下面的代码:

class Person

{

private String name;

private int age;

Person()

{

System.out.println(“A:name=”+name+”,age=”+age);

cry();

}

Person(String n)

{

name=n;

System.out.println(“A:name=”+name+”,age=”+age);

cry();

}

Person(String n,int a)

{

name=n;

age=a;

System.out.println(“A:name=”+name+”,age=”+age);

cry();

}

public void cry()

{

System.out.println(“cry.....”);

}

}

class PersonDemo

{

public static void main(String[] args)

{

Person p=new Person();

p.cry();

p.cry();

}

}

说一个笑话,孩子一出生就会哭。那么孩子以后还要哭怎么办呢?再来一个Person p1=new Person();不可的,这不是同一个孩子。所以这里的多个p.cry(),就表示孩子以后的哭。

 

总结:构造函数VS一般函数

a. 构造函数和一般函数在写法上有所不同。

b. 在运行上也有不同。构造函数是在对象一建立就运行,给对象初始化;而一般方法是对象调用才执行,给对象添加具备的功能。

c. 一个对象建立,构造函数只运行一次,而一般函数可以被对象调用多次。

 

构造代码块:

面试题

class Person

{

private String name;

private int age;

{

System.out.println(person code run);

}

Person()

{

System.out.println(“A:name=”+name+”,age=”+age);

cry();

}

Person(String n)

{

name=n;

System.out.println(“A:name=”+name+”,age=”+age);

cry();

}

Person(String n,int a)

{

name=n;

age=a;

System.out.println(“A:name=”+name+”,age=”+age);

cry();

}

public void cry()

{

System.out.println(“cry.....”);

}

}

class PersonDemo

{

public static void main(String[] args)

{

Person p=new Person();

Person p1=new Person(“lisi”)

}

}

运行结果:

person code run

A:name=null,age=0;

cry.....

person code run

A:name=lisi,age=0;

cry.....

 

上述程序中,红色代码称之为构造代码块。

通过程序我们可以发现:

构造代码块:

作用:给兑现进行初始化。

对象一建立就运行,而且优先于构造函数执行。

 

构造代码块和构造函数的区别:

① 构造代码块是给所有对象进行统一初始化。因此构造代码块中一般定义的是不同对象共性的初始化内容。

② 构造函数是给对应的对象初始化。

 

5、this关键字

我们观察以上代码,发现构造函数的参数名起得没有意义,阅读性很差。我们想通过参数名就能知道该参数代表的是什么,因此我们可以将n改成name,a改成age。构造函数体内的name=n应该改成name=name。但是又有一个问题,我们明明给对象初始了,有名字有年龄,但是打印出来发现还是默认的姓名和年龄,说明根本就没有赋值成功。这很容易理解:比如

Person(String name)

{

name=name;

}

这两个name都是局部变量。(局部中有就在局部中找)而我们现在想要的是将局部中的name赋值给成员变量name(该name是所创建对象的name)。This就可以完美的解决这个问题。可以这样做:

Person(String name)

{

this.name=name;

}

this:代表本类的对象,代表它所在函数所属对象的引用。简单来说,哪个对象在调用this所在的函数,this就代表哪个对象。有些方法被对象调用,这些方法中有涉及到属性,这些属性前面没有this.,是因为没有出现同名。

 

其实类里边的成员被使用全是由对象完成的。本类中的对象是this。(没讲到static之前)

 

class Person

{

private String name;

private int age;

person(String name)

{

this.name=name;

}

Person(String name,int age)

{

this.name=name;

this.age=age;

}

public void speak()

{

System.out.println(“name=”  +(this.)name+”,age=”+(this.)age);

(this.)show();

}

public void show()

{

System.out.println((this.)name);

}

}

class PersonDemo

{

public static void main(String[] args)

{

Person p=new Person(“lisi”);

Person p1=new Person(“zhangsan”);

p.speak();

p1.speak();

 

}

}

 

this关键字的应用:

(1)判断是不是同龄人

class Person

{

private String name;

private int age;

person(String name)

{

this.name=name;

}

Person(String name,int age)

{

this.name=name;

this.age=age;

}

public void speak()

{

System.out.println(“name=”  +(this.)name+”,age=”+(this.)age);

(this.)show();

}

public void show()

{

System.out.println((this.)name);

}

/*

这里需要注意的是:compare()的参数只有一个,因为是调用者和别人比,而不是调用者具备比较其他两个人年龄的功能。this.age表示调用者的年龄。

*/

public boolean compare(Person p)

{

return this.age==p.age;

}

}

class PersonDemo

{

public static void main(String[] args)

{

Person p1=new Person(“lisi”);

Person p2=new Person(“zhangsan”);

Boolean b=new p1.compare(p2);

System.out.println(b);

}

}

 

(2)构造函数之间的调用

....

Person(String name)

{

this.name=name;

}

Person(String name,int age)

{

this(name);//表示调用Person(String name),称之为 //this语句

//this.name=name;

this.age=age;

}

...

 

由于:构造函数之间可以利用this语句进行互相调用。因此多个构造函数我们可以只暴露一个出去。内部可以有很多初始化方式。但是避免相互调用出现死循环的情况。

 

6、static关键字

static关键字:

用于修饰成员(成员变量和成员函数)

 

被修饰后的成员具备一下特点:

a. 随着类的加载而加载。

类一被使用,静态就被加载进了内存。加载进去了,再一次使用类,就不会被重复加载。而且它的生命周期很长,类在,它在,类不在它不在。

b. 优先于对象存在。

c. 被所有对象所共享。

就好比说定义一个Person类,类中定义了成员变量country和姓名name。假设创建的对象姓名未知,国籍都是中国。那么每建立一个对象,堆内存中的都要开辟一个空间用于存储country,而这些country的值都一样,这样就比较占用空间浪费资源。因此:

我们将这个变量拎出来放到一个特定的位置:方法区(或者叫作共享区域/数据区/静态区)(里边存放都是类中的方法,类中的共享数据。也很好理解,之前我们画对象的时候,从来没往里边画过方法,比如:人有一个说话的方法,说自己的姓名和年龄,说的内容虽不同(放在堆内存中),但是说是一个共性行为(放在方法区))。谁要用就拿过来用,没有必要每一个对象中都存一份。这样一来,代码中几乎没有什么区别,就加一个修饰符。日后在开发中要分辨清楚什么数据是对象特有的,什么数据是对象共有的。

d. 可以直接被类名调用。

当然也可以被对象调用。

 

使用静态是需要注意:

不管在什么情况下:

a. 静态方法只能访问静态成员。但是非静态能访问静态。

b. 静态方法中不可以写this、super关键字。

c. 主函数是静态的。

 

实例变量和类变量:

实例变量就是没有被static修饰的成员变量。

类变量就是被static修饰的成员变量。

 

实例变量VS类变量

1、存放位置:

类变量随着类的加载而存在于方法区。

实例变量随着对象的建立存在于堆内存中。

2、生命周期:

类变量生命周期最长,随着类的消失而消失。

实例变量生命周期随着对象的消失而消失。

 

前面说了,主函数是静态的。下面我们来数一下神奇的主函数:

当我们使用java命令,会启动java虚拟机,java虚拟机回去调用main()。Jvm可以识别main()的固定形式。

 

main()也可以重载,但是必须要有能被jvm所识别的形式:public static void main(String[] args){};否则将会运行失败,但编译通过。

 

再来看一下main()的参数String[] args:

args其实是有来头的,是arguments的缩写版,早起外国的书上都是arguments。这不是重点,重点是String[] args确实是一个数组。可以通过打印语句来进行测试。但是它是个假数组,数组长度为0。但是我们可以在运行的时候,手动给它传值,它会接收,自动封装存储。

Class MainDemo

{

Public static void main(String[] args)

{

System.out.println(args.length);

System.out.println(args[0]);

}

}

如:在doc控制台中输入以下命令:

java MainDemo haha hehe heihei

运行结果是haha

 

静态什么时候用:

(1)什么时候定义静态变量(类变量)?

当对象中出现共享数据时,该数据被静态所修饰。

(2)什么时候定义静态方法?

当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的。

 

静态的应用-工具类:

现在假设我们写一个程序,程序中会涉及到求一个数组的最大值,于是我们在程序中写上求数组中元素最大值的功能;若干天以后,我们又得写一个程序,程序中依然设计到求一个数组的最大值。按照以前面向过程的思想,我们还得硬着头皮写,但现在是面向对象的思想,因此我们想说可以写一个工具类,里边定义的全是操作数组的功能(且全部是静态的,因为它们没有涉及到对象的特有数据),且为了避免使用者建立对象来调用工具类中的功能,强制将工具类的构造方法私有化,还有将其他不需要暴露出去的内容都通过private私有化。写程序开发时,什么时候需要,就什么时候用,不用创建对象,直接采用类名.的形式进行调用。

 

Ps:目前我们用的编辑工具是editplus,用dos控制台进行开发。编译和运行程序的时候都需要在某一个特定目录下完成。不管我们的程序中或者是要编译运行的点java文件中使用了什么其他.java文件中的内容,只要在该目录下能够找到,都可编译通过。不在的情况等讲到包时再说。

现在假设我们开发,定义的工具类是ArrayTool,而我们实际开发的程序中定义的类是ArrayToolDemo。我们在dos命令行窗口进行编译运行时可以采用两种方式:

(1)javac ArrayTool.java

     javac ArrayToolDemo.java

     java ArrayToolDemo

在当前所在路径下先后出现ArrayTool.class和ArrayToolDemo.class文件。

(2)javac ArrayToolDemo.java

     java ArrayToolDemo

在当前所在路径下同时出现ArrayTool.class和ArrayToolDemo.class文件。

其实在我们编译这个ArrayToolDemo.java文件的时候,如果这个文件当中用到了其它文件中内容时,它会在你指定目录下(通过set classpath=...;的形式)没有指定会在当前目录下找有没有ArrayTool.class文件。若没有虚拟机就会再找一次有没有ArrayTool.java文件,如果有先把它编译了,再编译自己。而运行的时候,虚拟机则会以classpath的值作为路径去找所要运行的.class文件。若没有指定则在当前目录下找。

 

有没有ArrayTool.java文件无所谓,关键要有ArrayTool.class文件。但是其实我们真正想充分利用这个工具类,我们只要这个.class文件是没有用的,因为我们看不懂,因此必须制作说明文档。

 

帮助文档的制作:(API文档的制作)

文档注释符:/** */

@:文档注释里边的其中一个特殊标识符,可以直接被文档注释工具提取并识别的。

Param:也是一个特殊的标识符,代表参数的意思。像这些标识符不用去记,等日后真正开发的时候,用eclipse就可以自动生成。

 

你想要把一个类生成帮助文档的话,这个类必须是public或者是protected修饰的。而且类中的非public或者是protected修饰的方法在生成的文档中也不可见。

 

/**

这是一个可以对数组进行操作的工具类,该类中提供了获取最大值、排序等功能。

@author 张三

@version v1.1

*/

public class ArrayTool

{

/**

空参数构造函数

*/

private ArrayTool()

{

}

/**

获取一个整型数组中的最大值。

@param arr 接收一个int类型的数组。

@return 会返回一个该数组中的最大值。

*/

public static void getMax(int num)

{

int max=0;

for(int x=1;x<arr.length;x++)

{

if(arr[x]>arr[max])

{

max=x;

}

}

return arr[max];

}

/**

获取一个整型数组中的最小值。

@param arr 接收一个int类型的数组。

@return 会返回一个该数组中的最小值。

*/

public static void getMax(int num)

{

int min=0;

for(int x=1;x<arr.length;x++)

{

if(arr[x]<arr[min])

{

min=x;

}

}

return arr[min];

}

/**

给int数组进行选择排序。

@param arr接收一个int类型的数组。

*/

public static void selectSort(int[] args)

{

for(int x=0;x<arr.length-1;x++)

{

for(int y=x+1;y<arr.length;y++)

{

if(arr[x]>arr[y])

swap(arr,x,y);

}

}

}

/**

给int数组进行冒泡排序。

@param arr接收一个int类型的数组。

*/

public static void bubbleSort(int[] args)

{

for(int x=0;x<arr.length-1;x++)

{

for(int y=0;y<arr.length-x-1;y++)

{

if(arr[y]>arr[y+1])

swap(arr,y,y+1);

}

}

}

/**

给数组中元素进行位置的置换。

@param arr接收一个int类型的数组。

@param a要置换的位置。

@param b要置换的位置。

*/

private static void swap(int[] arr,int a,int b)

{

int temp=arr[a];

arr[a]=arr[b];

arr[b]=temp;

}

/**

用于打印数组中的元素。打印形式是:[element1,element2,...]

*/

public static void printArray(int[] arr)

{

System.out.print(“[”);

for(int x=0;x<arr.length;x++)

{

if(x!=arr.length-1)

System.out.print(arr[x]+“,”);

else

System.out.println(arr[x]+“]”);

}

}

}

打开dos命令行写入:javadoc  -d   myhelp  (-author -version)ArrayTool.java

 

回车后,可发现当前目录下生成了一个新的文件夹,文件夹中有一大堆文件,它们是一个整体,应该打开index.html看,其他文件都是分页面。

 

Java自身也有个API文档(Application Program Interface),生成原理和方法相同,只不过它写了好多类,而将几个类又放到一个包中,如java.util。

 

 

静态代码块:

格式:

static

{

静态代码块中的执行语句。

}

特点:随着类的加载而执行,只执行一次。(因为头一次使用类,必须将类加载到内存中,第二次再使用类的话,就不需要再将类加载到内存中去了,已经在内存中了。)用于给类进行初始化。

 

问:那什么时候类才会别加载呢?

只有涉及到类里边的东西,才加载。

比如下面的情况累就不会被加载:

class StaticCode

{

static 

{

System.out.println(“a”);

}

public static void show()

{

System.out.println(“show run”);

}

}

class StaticCodeDemo

{

static

{

}

public static void main(String[] args)

{

StaticCode s=null;

}

Static

{

}

}

类类型变量无实体指向,变量存在无意义。类不会因为这个变量,而把类加载到内存中,那太浪费空间。

 

注意:静态代码块中也不能出现非静态内容,更不能出现this、super等关键字。

 

在主函数中new一个对象,程序运行的顺序:

在类中的静态代码块-->应用中的静态代码块-->构造代码块-->构造函数(后两步根据对象的个数可重复执行多次)

 

Person p=new Person(“zhangsan”,20);

该句话都做了什么事情?

1)因为new用到了Person.class。所以会先找到Person.class文件,并加载到内存中。

2)执行该类中的静态代码块,如果有的话,给Person类进行初始化。

3)在堆内存中开辟空间,分配内存地址。

4)在堆内存中建立对象的特有属性。并进行默认初始化。

5)对属性进行显示初始。

6)对对象进行构造代码块初始化。

7)对对象进行相应的构造函数初始化。

8)将内存地址赋给栈内存中的p变量。

 

 

7、单例设计模式

何为模式:解决某一类问题最行之有效的方法。

把几种模式综合应用一下就形成一个复杂的模式,称之为框架。

单例设计模式:解决一类在内存中只允许存在一个对象的问题。

 

想要保证唯一。

A、为了避免其他程序过多建立改类对象。先禁止其他程序建立该类对象。

B、还为了让其他程序可以访问到该类对象,只好在本地中自定义一个对象。

C、为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。

 

这三步怎么用代码实现呢?

A、将构造函数私有化。

B、在类中创建一个本类对象。

C、提供一个方法可以获取到该对象。

 

注意:

对于事物该怎么描述还得怎么描述。但需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。

 

单例设计模式有两种方式:

(1)饿汉式(开发时首选)

 类一进内存,就已经创建好了对象。

class Single

{

private Single(){}

private static Single s=new Single();

public static Single getInstance()

{

return s;

}

}

class SingleDemo

{

public static void main(String[] args)

{

Single ss=Single.getInstance();

}

}

 

(2)懒汉式(面试中会用到

 类一进内存,对象还没有存在,在调用getInstance方法时才创建。称之为对象的延时加载。

class Single

{

private Single(){}

private static Single s=null;

public static Single getInstance()

{

if(s==null)

s=new Single();

return s;

}

}

class SingleDemo

{

public static void main(String[] args)

{

Single ss=Single.getInstance();

}

}

注意:

但是我们都知道,cpu执行程序的时候,是切换式的,某一时刻只能执行一个任务,不同程序之间互相争夺cpu资源,cpu执行某一个任务执行多长时间由cpu决定。因此:假设现在有两个程序都有这个获取对象的方法。观察下图:

 

因此:类中建立了不只一个对象。

 

解决方案:上锁。进入一个就不让另一个再进入了。

(1)

class Single

{

private Single(){}

private static Single s=null;

public static synchronized Single getInstance()

{

if(s==null)

s=new Single();

return s;

}

}

class SingleDemo

{

public static void main(String[] args)

{

Single ss=Single.getInstance();

}

}

这种方式虽简单,但是每次都能判断这个“锁”,效率有点低。

(2)通过减少判断“锁”的次数,来提高效率。

class Single

{

private Single(){}

private static Single s=null;

public static Single getInstance()

{

if(s==null)

{

Synchronized(Single.class)

{

if(s==null)

s=new Single();

}

}

return s;

}

}

class SingleDemo

{

public static void main(String[] args)

{

Single ss=Single.getInstance();

}

}

 

 

2015-09-282015-11-07

0 0
原创粉丝点击