面向对象
------- android培训 java培训期待与您交流! ----------
一、面向对象概述
1. 定义
面向对象是一种编程思想,一种编程范型,编程范式有:面向过程、面向结构、面向对象等等不同的编程范型。而面向对象是现在主流的程序设计范型,java是完全的面向对象的,必须熟悉面向对象的思想,才能进行java程序的开发。
面向对象就是还原事物的特征,例如:一个人具有姓名、年龄的本身属性,还具有会说话、会游泳的技能方法,创建对象将这些属性和方法赋予给对象,我们需要调用的时候,再使用对象来调用就可以了。
又如:台灯能够提供照明,照明是台灯的技能方法,我们需要光的时候,就调用台灯,让台灯来发光就可以了,不需要我们自己发光照明。也就是说将发光照明的功能赋予到台灯这个对象上,我们仅仅是调用了这个对象,使用了对象的发光功能。
2. 特点
面向对象的特点有:封装性、继承性、多态性。
Java编程过程中,这三个特性是必须熟悉的,也会频繁的使用到。
二、类与对象
1. 类与对象的概述
类:是对一类事物的描述,对一类事物共性的抽取,是具有相同属性的事物的统称,是一种抽象。类是一种静态的概念。
对象:是对类的具体化。又称实例。对象是一种动态的机制。
例如:人就是一个类,你、我、他是这个类的具体实例,也就是对象。Java是面向对象的语言,每个对象都应该拥有它自己的属性和方法,就拿人来说,肤色、身高等是人的属性,吃、喝、玩、说等都是方法,也就是说属性描绘了类的特点,而方法描述了类的功能,体现在Java的类中就像下面的代码这样:
<SPAN style="FONT-SIZE: 18px">Person
{
private String name;
privateint age;
Person(String name,int age)
{
this.name=name;
this.age=age;
}
publicvoid say()
{
System.out.println(“我的姓名:”+name+”我的年龄”+age);
}
}</SPAN>
上面的代码是创建了一个类,name,age就是Person类中的成员属性,say是Person类中的成员方法。当我们需要将类实例化的时候,就需要创建一个实例化的对象,通过对象来调用类中的方法
<SPAN style="FONT-SIZE: 18px"> Person p1=new Person(“wangwu”,18);
p.say();</SPAN>
对象是可以重复的创建的,每次创建的对象都是不一样的。eg:
<SPAN style="FONT-SIZE: 18px"> Person p2=new Person(“liliu”,20);
p.say();</SPAN>
根据Person类可以创建出来很多的对象,每个对象的name和age属性都可以进行单独的设定,通过对象调用say方法,打印出来的内容也是不一样的。
2. 对象的创建
对象的创建在之前也说了,就是 类名 对象名=new 类名();这是创建了有对象名的对象。另外,还可以创建匿名对象,即没有对象名。例如:
- <SPAN style="FONT-SIZE: 18px"> Person p2=new Person(“liliu”,20);
- p.say();</SPAN>
Person p2=new Person(“liliu”,20);
p.say();
匿名对象创建是一次性的,创建之后,可以在后面直接调用类中的方法,匿名对象的使用处:对对象的方法只调用一次时,使用匿名对象比较简化;可以将匿名对象作为实际参数进行传递,也是为了简化代码。
一个小问题: 为什么使用匿名对象?为什么会简化代码?
对象的创建,需要在堆内存中开辟空间存放对象的内容,在栈内存中开辟空间存放对象变量,这些都是需要在内存中开辟空间的,而且对象存在的时间会很长。使用匿名对象,就免去了在栈内存中开辟空间,可以节省内存空间,而且在使用之后,对象就会立马被销毁,不会在内存中存在很长时间。所以说匿名对象可以简化代码,减小内存的开销,提高程序的的性能。
3. 类中的属性
属性:类中的属性分为全局变量、局部变量。
全局属性:实例变量是可以不进行初始化,比如一个整型的实例变量假如没有初始化,则默认值为0。
局部属性:局部变量是在方法内部声明的变量,生命期仅在方法内,方法结束后变量就消失了;局部变量必须初始化再使用,否则会报错,也就是说,假如你在方法内定义了一个局部变量,并且没有赋值,那么你在使用这个变量的时候一定得赋值,不然就报错了。同时,局部变量可屏蔽全局变量。
4. 类中的方法
方法中定义的是类的功能,方法就是类的行为。对于面向对象的开发来说,类中的方法是被封装起来的一个一个的功能。
- <SPAN style="FONT-SIZE: 18px">public void say()
- {
- System.out.println(“我的黑马梦想”);
- }</SPAN>
public void say()
{
System.out.println(“我的黑马梦想”);
}
方法是由方法头和方法体构成,方法头包括:访问控制符(如publicprivate等)、返回值类型、方法名、参数列表组成。声明为void的方法,返回值为空。类中的方法可以进行重载(Overload)和重写(Override亦叫覆写)。
重载:是指在同一个类中,具有相同的方法名,不同的参数列表的方法之间的一种机制。参数列表的不同体现在:类型不同、个数不同、顺序不同,只要满足任一一个,就可以进行方法重载。通过长方形、圆的面积的计算的代码来说明:
例子中计算圆面积的方法的参数和长方形面积方法的参数的个数不相同,但是使用的是同一个方法名area,同步传入不同的参数,实现不同的功能,这就是方法重载。方法的重载增强了程序的可读性和易于维护。
重写:重写是在继承中存在的,在两个类(子类和父类之间存在的关系)中,子类重写父类的方法,方法名相同,参数也相同的一种。
- <SPAN style="FONT-SIZE: 18px">public class B extends A {
- publicString a(String name) {
- return"welcome to you :" + name;
- }
- }
- class A {
- publicString a(String name){
- return"hello";
- }
- }</SPAN>
public class B extends A {
publicString a(String name) {
return"welcome to you :" + name;
}
}
class A {
publicString a(String name){
return"hello";
}
}
重写需要注意的问题:(1)静态类只能重写静态的(2)子父类方法必须是一模一样(权限、返回类型)(3)只能高权限方法重写低权限方法。(编译的时候不会报错,但是运行的时候不会达到你设定的想法)
三、面向对象之封装
1. 封装定义
封装,顾名思义就是隐藏。对象的属性,方法,没必要要外界知道,外界只需要在使用的时候调用这些对象的属性、方法即可,仅对外部提供公共访问方法。在面向对象的思想中,一切物体皆对象,我们以对象为单位进行编程,将这个对象所有的属性方法包装在一起,就是封装。
2. 封装的好处
(1)将对象内部的变化隐藏,外部只需要得到功能即可,内部的任何变化是不需要知道的。(2)便于使用。封装起来的对象只需要调用成员名就可以使用。(3)提高代码的重用性。只要得到对象,任何外部都可以调用对象中的公共成员。(4)提高安全性。将变化隐藏,外部不用修改内部的代码,只有调用即可,内部的异常在内部处理,或者抛出异常,使外部知道,提高了代码的安全。
3. 封装的原则
不需要对外提供的内容隐藏,把属性隐藏,只提供公共方法提取。
4. 构造函数
构造函数是与类名相同、无返回值的函数。
构造函数是为了给对象进行初始化,对象一建立就会运行与之对象的构造函数,当类无构造函数的时候,系统就会默认的加入空参数的构造函数。
构造函数只运行一次。
5. 构造代码块
构造代码块和构造函数的作用是一样的,只是构造代码块是没有函数名的,只有{}包围着的代码。
一般在构造代码块中的代码是对象具有共性的初始化。构造代码块是给所有的对象进行统一初始化的。
对象一建立,构造代码块就运行了,而且构造代码块优先构造函数运行。
例子中的运行结果为:
构造代码块在所有的对象建立的时候都会被打印一次
构造函数
构造代码块在所有的对象建立的时候都会被打印一次
构造函数
6. 关键字:this
this表示的本类的对象,代表它所在函数所属对象的引用。即:哪个对象在调用函数,this就代表哪个对象。
this的原理:this其实只是在栈内存中的一个隐藏变量,当对象调用的时候,将对象引用地址赋值给this变量。
this的应用:当定义类的功能中,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。
this可用于构造函数之间互相调用,但是this语句只能放在该构造函数的第一行
7. 关键字:static
Static是一个Java中关键字,用来修饰成员方法、成员属性的修饰词(只能修饰成员)。
被static修饰的成员变量是静态成员变量,与普通的变量不同,静态成员变量不在堆内存中,而是存在与内存中的方法区,而且静态成员变量可以使用 类名.静态成员 的方式调用
static的特征:(1)随着类的加载而加载(2)随着类的消失而消失(3)优先与对象而存在
注意:静态方法只能调用类中的静态成员,静态的方法只能被静态的方法覆盖(即重写)
静态代码块:与构造代码块有相同之处,静态代码块只需在构造代码块前添加static修饰,而且静态代码块比代码块先执行。
- <SPAN style="FONT-SIZE: 18px">Person
- {
- private String name;
- privateint age;
- static
- {
- this.name=name;
- }
- }</SPAN>
Person
{
private String name;
privateint age;
static
{
this.name=name;
}
}
注意:类的加载过程:
1、先装载.class文件,创建Class对象,对静态数据(由static声明的)进行初始化,而且只进行一次初始化。
2、newBuild()在堆上进行空间分配。
3、执行非静态块。
4、执行所有方法外定义的变量的初始化。
5、执行构造器。
四、 面向对象之继承
1. 定义
继承类似子父关系,只有类与类之间有所属的关系,才可以继承。子类具有父类的属性,但是子类中还有自己的属性。
比如:人作为一个类,所有的人都有name、age、sex属性,工人会盖楼,警察会抓小偷,这是他们各自的技能方法,但是工人,警察都是人,工人和警察继承人这个类,本身就具有了name、age、sex属性了,在各自的类中只用再创建各自特有的方法即可。
继承就是将一类事物的共性提取出来到一个类中,然后具体的类继承这个类。两个类之间通过extends关键字实现继承
2. 作用
继承提高了代码的复用性。而且让类与类之间产生了关系,因此有了面向对象的第三个特征:多态。
3. 子父类关系
首先举例说明一个问题:
ExtendsTest继承自B,B继承自A,当实例化ExtendsTest的时候,依次打印出了A、B、ExtendsTest构造器中的内容,说明:构造器被依次调用了,这是为什么?因为当类实现继承时,默认的会将基类的一个子对象传给子类,而子类需要对这个子对象进行初始化,所以需要调用父类的构造器,但是,这一切都是隐式进行的,我们看不到,不过可以从实验中得出结论:在对子类进行初始化的时候,会先调用父类的构造器。如果父类构造器需要传递参数,则使用super关键字来实现就行了。
子类不能继承父类私有的域或者方法。如果想要继承父类的私有对象,只能将private改成protected,因为protected的权限控制在包内。因此一般情况,用到继承的话,最好将父类中的域声明为私有(private,因为一般情况不需要继承成员变量),将方法声明为public,方便继承。
当子类对象调用一个方法时,如果子类没有,则去调用父类的同名方法,但是调用者保持是子类
4. 关键字:final
Final可以修饰类、方法、变量。
被final修饰的类不可以被继承。
被final修饰的方法不可以被复写
被final修饰的变量只可以被赋值一次,即常数变量。通常的类中不变的数被final修饰,作为一个常数存在:
5. 抽象类(abstract)
当多个类中出现相同的功能,但是主体相同的时候,这时候可以向上抽取共性,定义成一个抽象类。
抽象类注意的几个点:(1)抽象方法必须放在抽象类中(2)抽象类不能被创建对象(3)在抽象类中定义抽象方法时,方法无方法体,也无{}。(4)抽象类中的方法要被调用,必须由子类复写所有的抽象方法。
接口和抽象类这两个概念总是被放在一起讨论,因为他们有很多相似的地方,可以说接口本身就是完全抽象的,它要比抽象类更加“抽象”,为什么这么说?抽象类是一种类,里面除了有抽象方法外,还可以有具体的方法,而接口里面必须都是抽象的方法,尽管有时并没有显示的用abstract关键字声明。此处我们提到抽象方法,在Java中,凡是声明为形如:abstract void function()的方法,都是抽象方法,包含抽象方法的类就是抽象类,可以这么总结:抽象类中是可以没有抽象方法的;有抽象方法的类必须是抽象类;抽象类不一定有实体方法。
6. 接口(interface)
接口是特殊的抽象类,因为接口中的方法都是抽象的方法。
接口中的成员都是public的,
接口特点:类只能继承一个类,但是可以实现多个接口,接口与接口也可以有继承关系。
之前我们知道,采用interface为我们提供了一种将抽象与实现分离的结构化的方法,但是interface的作用远不止此,在Java中接口解决了一个非常重要的问题:多继承。Java继承机制不允许多重继承,所以如果想要整合不同类的功能,就需要使用接口。举个例子来说明:
我们可以看到:
1、Hero类中拥有了所有类的功能。
2、Hero可以和它实现的这些接口进行相互转换,当我们将hero对象做参数,传入Adventure类的各个方法时,Hero类向上转型了。总结下使用接口的好处:
3、接口可以实现向上转型,多个具有共同属性的类可以将它们的共同点提取出来,做成抽象,这样层次分明,统一管理。
4、接口不具有任何实现,最适合做基类。
总结一下抽象类与接口的区别和联系:
a) 抽象类是类,可以有实体方法。
b) 抽象类不能实现多继承,而接口可以。
c) 如果需要创建不带任何方法定义和成员变量的基类,则使用接口,如果类中需要有部分具体的实现,则使用抽象类。
d) 如果事先想要将某类设计为一个基类,那么首选接口。
五、 多态
1. 定义
事物存在多种体现形态,比如:男人也是人;小轿车也是车;杂志也是书。
多态一般使用在子父类的引用中,父类接收子类对象 父类 变量名=new 子类();//类型进行了提升,类似于隐式的转换。
多态的好处:大大的提高了程序的扩展性。
注意:多态编译的时候,父类中无与子类中相同成员方法名称的成员函数时,对像是不能调用的,运行时,对象调用先运行同名子类成员函数。
2. 内部类
内部类的意思就是将类的定义放在另一个类的内部。有时合理的内部类使用会使代码更加简洁,令程序更加巧妙。而且作为外部类的成员,内部类可以访问外部类私有的成员变量。
外部类创建内部类对象的格式:
静态内部类不可以引用外部类的非静态成员。
当内部类成员为静态时,内部类必须为静态的。
使 用匿名内部类应该注意:
a) 匿名内部类不能有构造方法
b) 匿名内部类不能定义任何静态成员、方法和类。
c) 匿名内部类不能是public,protected,private,static。
d) 只能创建匿名内部类的一个实例。
e) 一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
f) 因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
举例说明一下匿名内部类:
- <SPAN style="FONT-SIZE: 18px">ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
- timer.scheduleAtFixedRate(
- new Runnable(){
- public void run(){
- currentLamp = currentLamp.dark();
- }
- },
- 5,
- 5,
- TimeUnit.SECONDS);</SPAN>