03_初级_Java之美[从菜鸟到高手演变]之类与对象(一)
来源:互联网 发布:网络乞丐 编辑:程序博客网 时间:2024/05/21 10:36
对于像Java这样的纯面向对象语言,类是最基本的抽象单位,一直以来我总是希望自己能写出一篇文章,来好好的梳理下自己对类和对象的理解。今天,这个愿望似乎要实现了!不知从哪个地方写起,因为这方面设计的东西太多了,说到类,就不难想到继承、多态、封装,就不难想到作用域及生命周期,很多的东西一下子涌上心头,让我不知道该从哪儿下手。本章系Java之美[从菜鸟到高手演变]系列之类与对象,希望通过我的分析,能让读者朋友们更加牢固记住相关的知识点,掌握类与对象方面的精髓!
阅读过程中有任何问题,请联系egg:
邮箱:xtfggef@gmail.com 微博:http://weibo.com/xtfggef
如有转载,请说明出处:http://blog.csdn.net/zhangerqing
一、类的创建及初始化
类通常是一类事物的抽象,如人就是一个类,你、我、他是这个类的具体实例,也就是对象。在Java中我们可以通过形如:class A {}来创建一个类,我们说过Java是面向对象的语言,每个对象都应该拥有它自己的属性和方法,就拿人来说,肤色、身高等是人的属性,吃、喝、玩等都是方法,也就是说属性描绘了类的特点,而方法描述了类的功能,体现在Java的类中就像下面的代码这样:
[java] viewplaincopy
1. public class Person {
2.
3. String name;
4. int age;
5.
6. void eat(){
7.
8. }
9. }
在面向对象的思想中,一切物体皆对象,我们以对象为单位进行编程,将这个对象所有的属性方法包装在一起,就是封装。一般情况,我们通过类的构造器来创建类对象,构造器是一个拥有和类名同样的名字的方法,我们可以对它传递参数,进行一些初始化工作,如,当我们需要在创建对象的时候,初始化其姓名及年龄,我们可以这样来做:
[java] viewplaincopy
1. public class Person {
2.
3. String name;
4. int age;
5.
6. public Person(String name,int age){
7. this.name = name;
8. this.age = age;
9. }
10. void eat(){
11.
12. }
13. }
测试类中:
[java] viewplaincopy
1. public class ClassTest {
2.
3. public static void main(String[] args) {
4. Person person = new Person("egg",23);
5. }
6. }
new 操作符会为我们在内存中开辟空间,person是对象名,也是引用,在栈上分配,指向有new在堆上分配的内容,具体的JVM内存管理,请看我的另一篇博文:JVM内存管理与垃圾回收,里面有详细的介绍,此处非重点,不去深讲。我们再来分析一下这个过程:当调用new Person()时,编译器首先检查下原类Person中是否有Person()构造方法,此处因为有public Person(String name,int age),所以new的时候,直接调用的该方法,但是很多时候,我们并没有显示声明构造方法,此时,编译器在调用的new Person()的时候,会自动为我们的Person类添加一个无参的空Person()构造方法:Person(){},来执行类的构造过程。说到构造方法,我们来看看下面的这段代码:
[java] viewplaincopy
1. public class Person {
2.
3. public Person(int id) {
4. System.out.println("person(" + id + ")");
5. }
6.
7. public static void main(String[] args) {
8. Build b = new Build();
9. }
10. }
11.
12. class Build {
13. Person p1 = new Person(1);
14.
15. public Build() {
16. System.out.println("this is build's block!");
17. Person p2 = new Person(2);
18. }
19.
20. Person p3 = new Person(3);
21.
22. }
此处我主要想说明,用构造器创建类和变量的初始化顺序,该程序输出:
person(1)
person(3)
this is build's block!
person(2)
说明:不论变量放在哪儿,都会先于任意一个方法的执行前执行,包括构造方法,而构造方法是一个类必须会执行的方法,不需要显示的进行调用。同时,不论变量在哪儿分布,只要在方法外部,就一定先于方法初始化。说到这儿,我们不得不谈一下另一个关键的知识点静态块和非静态块。二者都有很简单的声明方式,只需一对大括号{}就行,静态块的话,在前面加static关键字,我们写个小程序来看看:
[java] viewplaincopy
1. public class Person {
2.
3. /*静态块*/
4. static{
5. System.out.println("this is static block!");
6. }
7. /*非静态块*/
8. {
9. System.out.println("this is non-static block!");
10. }
11. public Person(int id) {
12. System.out.println("person(" + id + ")");
13. }
14. public static void main(String[] args) {
15. Person p1 = new Person(1);
16. Person p2 = new Person(2);
17. }
18. }
该程序输出:
this is static block!
this is non-static block!
person(1)
this is non-static block!
person(2)
说明什么问题?观察一下:我们new了两个对象,可是静态块只执行了一次,而非静态块执行了两个,且都是在调用构造器之前。我们似乎得出了一些结论:静态块是在类的装载时执行的(装入.class文件后),且只执行一次。而非静态块是在调用构造方法之前执行的,每生成一个实例对象,就会调用一次非静态块。此处,我想引入一个很重要的知识点:static关键字。一般来说,被声明为static的变量或者方法,或者前面说的块,都属于类变量、类方法,属于类的属性信息(在方法区分配内存)。如静态块一样,其它的静态数据也具有这个特点:初始化只在类装载的时候执行一次。对于类变量和类方法,还有一个重要的特点就是,外部对象对他们的引用可以直接通过类名来调用,如下面的代码:
[java] viewplaincopy
1. public class Person {
2. static int id = 10;
3. }
4.
5. class Test{
6. public static void main(String[] args) {
7. System.out.println(Person.id);
8. }
9. }
除了使用new操作符,我们还有一些其它方法来创建类,如Java的反射机制,我们会有专门的博文来介绍相关的知识点。
我们总结下对象的创建过程:(含顺序)根据下面这个程序的输出:
[java] viewplaincopy
1. public class Person {
2.
3. public Person(int id) {
4. System.out.println("person(" + id + ")");
5. }
6. }
7.
8. class Build {
9.
10. /*静态块*/
11. static{
12. System.out.println("this is static block!");
13. }
14. /*非静态块*/
15. {
16. System.out.println("this is non-static block!");
17. }
18. Person p1 = new Person(1);//------------1-----------
19.
20. public Build() {
21. System.out.println("this is build's block!");
22. Person p2 = new Person(2);
23. }
24.
25. Person p3 = new Person(3);
26.
27. public static void main(String[] args) {
28. Build b = new Build();
29. }
30. }
this is static block!
this is non-static block!
person(1)
person(3)
this is build's block!
person(2)
1、先装载.class文件,创建Class对象,对静态数据(由static声明的)进行初始化,而且只进行一次初始化。
2、new Build()在堆上进行空间分配。
3、执行非静态块。
4、执行所有方法外定义的变量的初始化。
5、执行构造器。
我们再来看个例子:将static放在上述例子的注释1行之前,输出会有变化:
this is static block!
person(1)
this is non-static block!
person(3)
this is build's block!
person(2)
正好验证了我们上面的结论,总体来说执行顺序为:静态块->静态属性->非静态块->属性->构造器。接下来我们分析一下类的属性和方法。
属性:
类中的属性一般分为类属性(全局变量)、实例属性(全局变量)、局部属性(局部变量)。<我是这么分的,尽管有人不这么分,但是分法无所谓,理解它们的含义最重要>.
类属性:前面已经说过就是那些声明为static的属性,在整个过程中只进行一次初始化,在内存中只开辟一个空间,不论在哪儿调用,值保持一致。一旦被修改,所有引用它的地方都会跟着修改。一般直接通过类名进行调用。
实例属性:实例变量是可以不进行初始化,比如一个整型的实例变量假如没有初始化,则默认值为0;而局部变量假如不赋初值语法上是通过的,但是在使用这个变量是程序就报错了。实例变量在堆和栈中都分配内存空间,在堆当中分配的是对象本身,而栈中则是对这个对象的引用。
局部属性:局部变量是在方法内部声明的变量,生命期仅在方法内,方法结束后变量就消失了;局部变量必须初始化再使用,否则会报错,也就是说,假如你在方法内定义了一个局部变量,并且没有赋值,那么你在使用这个变量的时候一定得赋值,不然就报错了。同时,局部变量可屏蔽全局变量。
方法:
方法就是类的行为,形如:
[java] viewplaincopy
1. public void say(){
2. dosomething;
3. ...
4. }
由方法头和方法体构成,方法头包括:访问控制符(如public private等)、返回类型(返回类型决定该方法在调用后产生的结果的类型)、方法名、参数列表组成。声明为void的方法,返回值为空。在特殊的情况下,我们可以为方法添加一些特殊的关键字以实现特殊的功能,如synchronized、final、static、abstract等等。方法方面我只想介绍两个重要的方面:重载(Overload)和重写(Override亦叫覆写)。
重载:
是指在同一个类中,具有相同的方法名,不同的参数列表的方法之间的一种机制。参数列表的不同体现在:类型不同、个数不同、顺序不同,只要满足任一一个,就可以进行方法重载。
[java] viewplaincopy
1. public class AreaCal {
2.
3. /* 计算长方形的面积 */
4. public int area(int width, int height) {
5. return width * height;
6. }
7.
8. /* 计算正方形的面积 */
9. public int area(int edge) {
10. return edge * edge;
11. }
12.
13. /* 计算圆的面积 */
14. public double area(float radius) {
15. return 3.14 * radius * radius;
16. }
17. }
如例所示,同一个方法名area,同步传入不同的参数,实现不同的功能,这就是方法重载,用这样的机制有什么好处?个人感觉就是在模型上的一种统一,同样的功能,调用同样的方法,只需传入不同的参数,增强了程序的可读性和易于维护,当有很多个功能相似的方法的时候,如果我们为每个方法设计一个名称,想通过名称来区分它们的话,会很糟糕,而且会让人觉得程序的可读性差,设计不够巧妙!此处问题来了:我们可不可通过方法的返回值来区别方法重载?让我们看个例子:
[java] viewplaincopy
1. public int area(int width, int height) {
2. return width * height;
3. }
4.
5. public float area(int width, int height){
6. return width * height;
7. }
当其他环境调用这两个方法时,并不会先得到他们的返回值,返回值只有在方法执行完毕才返回,在调用之初,编译器无法判断他们的区别,所以只能当同名方法来处理:报错!所以试图通过返回值来进行方法重载是不正确的!
重写:
重写是在继承中存在的,在两个类(子类和父类之间存在的关系)中,子类重写父类的方法,方法名相同,参数也相同的一种机制。
[java] viewplaincopy
1. public class B extends A {
2.
3. public String a(String name) {
4. return "welcome to you :" + name;
5. }
6. }
7. class A {
8. public String a(String name){
9. return "hello:"+name;
10. }
11. }
当子类继承了父类后,想对父类的方法功能进行扩展,就可以使用重写,这样做的目的就是:当你需要扩展新的功能的时候,不需要新建方法,在父类的方法中进行补充,这样一种面向对象思维的体现,不用重写同样可以达到这样的效果,但是用了重写更加符合OO思想。类似的还有一种概念,叫隐藏:当子类和父类方法名相同,参数不同时,子类隐藏父类的方法实现,这就是一种机制,一种叫法,没什么实际意义,相当于我们新建了方法,只是方法名和父类的相同,但是不是父类的方法的实现。
Java中不定参数调用
有的时候,我们不能确定方法的参数个数,我们可以采取这种机制(String ... value),如下面的代码:
[java] viewplaincopy
1. public class B{
2.
3. public static String a(String ... value) {
4. return "welcome to you :" + value;
5. }
6.
7. public static void main(String[] args) {
8. System.out.println(a("egg"));
9. }
10. }
打印出来的结果是:welcome to you:[Ljava.lang.String;@61de33。后面这部分信息一看就是数组的信息,所以我们继续研究,发现:当你将参数写出String ... value的形式的时候,已经默认相当于:String[] value 因此你需要在方法内部获取的数据的时候写成数组下标的形式:
[java] viewplaincopy
1. public static String a(String ... value) {
2. return "welcome to you :" + value[0];
3. }
当有多个参数时,同样用下标来取值:
[java] viewplaincopy
1. public class B {
2.
3. public static String a(String... value) {
4. return "welcome to you :" + value[0] + value[1] + value[2];
5. }
6.
7. public static void main(String[] args) {
8. System.out.println(a("egg", ":niu", ":baby!"));
9. }
10. }
这种机制在有些开发中特别有用,降低代码量,使程序更加简洁,不过有时会牺牲一点可读性。
二、类与对象的关系
经过上面这么多的演练,我们总结下类和对象的关系:看看下面的代码:
[java] viewplaincopy
1. public class B {
2.
3. public static void main(String[] args) {
4. B b = new B();
5. A a = new A();
6. }
7. }
8.
9. class A {
10.
11. }
这段代码中,A和B都是类,a和b属于对象,他们之间是种什么关系呢?
1、类是一类具有相同属性的事物的统称,是一种抽象。
2、对象是类的具体体现,又称实例。
3、类是一种静态的概念,而对象是一种动态的机制。
每个人都有不同的看法,还有什么关系,希望大家补充,笔者真心希望各位读者能认真思考,积极提出宝贵的建议!因为有不少读者提出之前的博文篇幅过长,希望我尽量写的短一点儿,方便阅读,所以我将其它内容放在了下一篇,希望朋友们能够继续阅读!
本章完。
分享到:
· 上一篇:Java之美[从菜鸟到高手演变]之线程同步的引入
· 下一篇:Java之美[从菜鸟到高手演变]之类与对象(二)
顶
2
踩
0
查看评论
10楼 新晓沫 昨天 17:40发表 [回复]
楼主,上面讲到每生成一个实例对象,就会调用一次非静态块。但是在下面的代码中
class Person {
public Person(int id) {
System.out.println("person(" + id + ")");
}
}
public class Build {
/*静态块*/
static{
System.out.println("this is static block!");
}
/*非静态块*/
{
System.out.println("this is non-static block!");
}
Person p1 = new Person(1);//------------1-----------
public Build() {
System.out.println("this is build's block!");
Person p2 = new Person(2);
}
Person p3 = new Person(3);
public static void main(String[] args) {
Build b = new Build();
}
}
只是在 Person p1 = new Person(1);之前执行了非静态的代码块,为啥在Person p3 = new Person(3);前面不执行呢?
9楼 yangli920519 3天前 10:06发表 [回复]
对于我这种初学者是很不错的只是总结。。也想向楼主讨教一些初学者的学习方法。
Re: 终点 前天 18:29发表 [回复]
回复yangli920519:好好浏览下我的博客,里面有答案!
8楼 岑逸 2013-01-0117:00发表 [回复]
博主的main方法应在主类里边。。。我们新手差点弄错了。。。
Re: 终点 2013-01-01 20:55发表 [回复]
回复cenyi2012:你是说哪段代码?
Re: 岑逸 2013-01-01 22:04发表 [回复]
回复zhangerqing:下面的同学指出来了。。。呵呵
Re: 岑逸 2013-01-01 22:03发表 [回复]
回复zhangerqing:构造器创建类和变量的初始化顺序
Re: 岑逸 2013-01-01 22:02发表 [回复]
回复zhangerqing:[java] view plaincopy
public class Person {
public Person(int id) {
System.out.println("person(" + id + ")");
}
}
class Build {
Person p1 = new Person(1);
public Build() {
System.out.println("this is build's block!");
Person p2 = new Person(2);
}
Person p3 = new Person(3);
public static void main(String[] args) {
Build b = new Build();
}
}
Re: 终点 2013-01-01 23:24发表 [回复]
回复cenyi2012:好的,已经改正,感谢这几位提出问题的朋友,这段时间比较忙,没有时间更新博客,等过段时间,博客会继续更新的!
Re: 岑逸 6天前 09:11发表 [回复]
回复zhangerqing:嗯,感谢博主百忙之中,把这些重要的基础知识总结出来供大家学习!谢谢!~
7楼 water_lang 2012-12-24 11:22发表 [回复]
看了这文章,然后就相起的think in java上的那一章专门讲的类初始化,还是不错,支持下
6楼 xqiangzhu 2012-12-23 21:13发表 [回复]
我觉得还是篇幅长一点好,看完感觉好有成就感。分开篇幅后感觉一篇也也没什么知识点!
5楼 Xxxxbf 2012-12-23 13:13发表 [回复]
public class Person {
public static void main(String[] args) {
Build b = new Build();
}
public Person(int id){
System.out.println("person("+id+")");
}
}
class Build{
Person p1 = new Person(1);
public Build(){
System.out.println("this is build's block!");
Person p2 = new Person(2);
}
Person p3 = new Person(3);
}这样就可以....
4楼 Style_angle 2012-12-21 22:07发表 [回复]
应该是
class Person {
public Person(int id) {
System.out.println("person(" + id + ")");
}
}
public class Build {
Person p1 = new Person(1);
public Build() {
System.out.println("this is build's block!");
Person p2 = new Person(2);
}
Person p3 = new Person(3);
public static void main(String[] args) {
Build b = new Build();
}
}
疏忽了,同志
3楼 Style_angle 2012-12-21 22:02发表 [回复]
public class Person {
public Person(int id) {
System.out.println("person(" + id + ")");
}
}
class Build {
Person p1 = new Person(1);
public Build() {
System.out.println("this is build's block!");
Person p2 = new Person(2);
}
Person p3 = new Person(3);
public static void main(String[] args) {
Build b = new Build();
}
}
代码运行有错误吧
2楼 麻豆坊 2012-12-1710:53发表 [回复] [引用] [举报]
详细不错
1楼 liuyanfov 2012-12-15 16:40发表 [回复]
不错~
- 03_初级_Java之美[从菜鸟到高手演变]之类与对象(一)
- 04_初级_Java之美[从菜鸟到高手演变]之类与对象(二)
- Java之美[从菜鸟到高手演变]之类与对象(一)
- Java之美[从菜鸟到高手演变]之类与对象(一)
- Java之美[从菜鸟到高手演变]之类与对象(一)
- 01_初级_Java之美[从菜鸟到高手演变]之Java学习方法
- 02_初级_Java之美[从菜鸟到高手演变]之字符串的处理
- Java之美[从菜鸟到高手演变]之类与对象(二)
- Java之美[从菜鸟到高手演变]之类与对象(三)
- Java之美[从菜鸟到高手演变]之类与对象(二)
- Java之美[从菜鸟到高手演变]之类与对象(三)
- Java之美[从菜鸟到高手演变]之类与对象(二)
- Java之美[从菜鸟到高手演变]之类与对象(三)
- Java之美[从菜鸟到高手演变]之字符串
- Java之美[从菜鸟到高手演变]之智力题
- Java之美[从菜鸟到高手演变]之Exception
- Java之美[从菜鸟到高手演变]之字符串
- Java之美[从菜鸟到高手演变]之Exception .
- CodeForeces 35D
- javascript学习3——javascript面向对象(上)
- 01_初级_Java之美[从菜鸟到高手演变]之Java学习方法
- 02_初级_Java之美[从菜鸟到高手演变]之字符串的处理
- 硬盘安装CentOS 6.0(图)
- 03_初级_Java之美[从菜鸟到高手演变]之类与对象(一)
- 提升Java性能的技巧
- 04_初级_Java之美[从菜鸟到高手演变]之类与对象(二)
- Swinghacks——自定义边框
- Android Wi-Fi Display(Miracast)介绍
- 迎接2013年
- Cassandra 使用Thrift API操作数据, 读写多行(range区间)多列(slice切片)数据
- ANDROID DisplayManager 服务解析一
- paip.获取当前实际北京时间API