原文链接:http://blog.csdn.net/u011593278/article/details/9738009
1. 面向对对象的三大特征:封装、继承、多态;
2. 封装:隐藏对象的属性和实现细节,仅对外公开接口,控制程序中属性的读和修改的访问权限,将抽象得到的数据和行为相结合,形成“类”,其中数据和方法都是类的成员。封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问方式来使用类的成员。
封装的基本要求是:把所有的属性私有化,对每个属性提供getter和setter方法,如果有一个带参的构造函数的话,那一定要写一个不带参的构造函数(有带参数的构造器时,系统就不会再自动生成不带参数的构造器了)。
例如:家里的电视,结构复杂,里面乱七八糟的零件一大堆,但是电视使用起来就是那么简单,为什么?那是因为创维把电视来了个封装,把对用户不重要的东西用个电视机壳把它装起来了,然后给你个遥控器,通过这个遥控器,你就可以操作这台电视机了。也就是说你不需要理解电视内部的构造原理和工作原理,你只要会按按钮就可以了。
分析:封装的必要性,封装可以提高类的安全性,避免其他类修改数据,这个的依据是因为对象可以直接调用该类的属性,已达到通过属性修改数据的目的,例如下所示:
编写一个教师类,要求:
(1)具有属性:姓名、年龄
(2)具有行为:自我介绍
(3)教师的最小年龄要求:22岁
代码示例:
public class Teacher{
public Stringname;//教员姓名
public int age;//年龄
/**
*返回自我介绍的内容
*/
public Stringintroduction(){
return"大家好!我是"+name+",我今年"+age+"岁";
}
}
编写一个测试类,要求:实例化一个教员对象,并对其初始化,并在控制台输出该教员的自我介绍,如下所示:
代码示例:
public class TeacherTest{
public static void main(String[]args){
Teacherteacher=new Teacher();
teacher.name="张三";
teacher.age=9;
System.out.println(teacher.introduction());
}
}
结果:大家好!我是张三,我今年9岁;
分析:因为没对属性进行封装,所以运行结果不满足教员的最小年龄要求,要满足此要求,就需要通过对属性的封装来实现,修改Teacher类如下:
代码示例:
public class Teacher {
private Stringname;//教员姓名
private int age;//年龄
public int getAge() {
returnage;
}
public void setAge(int age) {
if (age < 22) {
System.out.println("错误!最小年龄应为22岁!");
this.age = 22;//如果不符合年龄要求,则赋予默认值
}else {
this.age = age;
}
}
public String getName() {
returnname;//返回教员姓名
}
public void setName(String name) {
this.name = name;//设定教员姓名
}
public String introduction() {
return"大家好!我是" +name +",我今年" +age +"岁";
}
}
编写一个测试类,和上面不一样的是,下面的测试是对象调用方法,而不是给对象中的属性赋值。
public class TeacherTest{
public static void main(String[]args){
Teacherteacher=new Teacher();
teacher.setName("张三");
teacher.setAge(9);
System.out.println(teacher.introduction());
}
}
结果:错误!最小年龄应为22岁!
大家好!我是张三,我今年22岁;
分析:封装由于隐藏了自己的属性,其他类不能调用private属性,所以使用隐藏属性的唯一途径就是生成get/set方法,供外界调用,这样不仅可以在get/set方法中设置限定代码,而且还有利于程序的维护和模块化;
问题:我给public属性的也加上set/get方法,不也一样可以限定年龄吗?
解答:如果这样的话,那么测试方法中可以用两种方式初始化Teacher对象了,
代码示例:(第一种方法)
public class TeacherTest{
public static void main(String[]args){
Teacherteacher=new Teacher();
teacher.name="张三";
teacher.age=-4;
System.out.println(teacher.introduction());
}
}
结果:大家好!我是张三,我今年-4岁
代码示例:(第二种方法)
public class TeacherTest{
public static void main(String[]args){
Teacherteacher=new Teacher();
teacher.setName("张三");
teacher.setAge(-4);
System.out.println(teacher.introduction());
}
}
结果:错误!最小年龄应为22岁!
大家好!我是张三,我今年22岁
分析:这样的话set/get方法中所有的限定就没有用了,别人可以用第一种方法(直接对属性的访问)来修改类的限制功能,所以,封装(私有的属性,共有接口方法)能带来好处;
知识扩展:使一个对象不可变需要有3个步骤:
(1)数据成员必须是私有的,防止被外部的代码直接地操纵。(private)
(2)不能提供setter方式,目的就是不允许对被封装的数据的变更。(可以在类中的属性直接初始化)
(3)类本身必须是最终的,声明类为最终的可以防止另一个类创建不可变类的可变子类。(final关键字:不能被继承)
3. 访问修饰符(access modifier)。
1) public(公共的):被public所修饰的属性和方法可以被所有类访问。
2) protected(受保护的):被protected所修饰的属性和方法可以在类内部、相同包以及该类的子类所访问。
3) private(私有的):被private所修饰的属性和方法只能在该类内部使用
4) 默认的(不加任何访问修饰符):在类内部以及相同包下面的类所使用。
4. 如果一个java源文件中定义了多个类,那么这些类中最多只能有一个类是public的,换句话说,定义的多个类可以都不是public的。
5. 方法重载(Overload)。表示两个或多个方法名字相同,但方法参数不同。方法参数不同有两层含义:1)参数个数不同。2)参数类型不同。注意:方法的返回值对重载没有任何影响。
6.构造方法重载:只需看参数即可。如果想在一个构造方法中调用另外一个构造方法,那么可以使用this()的方式调用,this()括号中的参数表示目标构造方法的参数。this()必须要作为构造方法的第一条语句,换句话说,this()之前不能有任何可执行的代码。
程序示例:
package com.zp.base;
public class ConstructOverload {
public ConstructOverload(){
this(4);//必须写在第一行,调用了第二个带参的构造方法;
System.out.println("test");
}
public ConstructOverload(inti){
System.out.println(++i);
}
public static void main(String []args){
ConstructOverloadc1 = new ConstructOverload();
ConstructOverloadc2 = new ConstructOverload(3);
}
}
结果:5 test 4
7.继承(Inheritence):Java是单继承的,意味着一个类只能从另一个类继承(被继承的类叫做父类【基类】, 继承的类叫做子类),Java中的继承使用extends关键字。
8. 当生成子类对象时,Java默认首先调用父类的不带参数的构造方法,然后执行该构造方法,生成父类的对象。接下来,再去调用子类的构造方法,生成子类的对象。【要想生成子类的对象,首先需要生成父类的对象(父类得有无参的构造方法),没有父类对象就没有子类对象。比如说:没有父亲,就没有孩子】。
程序示例:
package com.zp.base;
public class Parent {
public Parent(){
System.out.println("parent...");
}
}
package com.zp.base;
public class Child extendsParent {
public Child(){
System.out.println("Child...");
}
public static void main(String []args){
Childc = new Child();
}
}
结果:parent... Child...
子类有参数的构造方法和上述结果一样,这样就能得出:无论子类是什么构造方法,都可以创建父类对象,然后生成子类对象;但是如果将父类的构造器改成有参构造器,如下所示:
public class Parent {
public Parent(int a){
System.out.println("parent...");
}
}
则会出现错误如下所示:
Implicit super constructor Parent() is undefined. Must explicitly invokeanother constructor.
解决方案:通过在子类构造器中使用super()方法,调用父类带参数构造器
程序示例:
public class Parent {
public Parent(int a){
System.out.println("parent...");
}
}
package com.zp.base;
public class Child extendsParent {
public Child(){
super(1); //此处调用父类带参数的构造方法
System.out.println("Child...");
}
public static void main(String []args){
Childc = new Child();
}
}
结果:parent... Child...
9. 关键字:super表示对父类构造方法的调用;this表示对当前类的构造器的调用,必须写在构造器的第一条语句上。
10. 如果子类使用super()显式调用父类的某个构造方法,那么在执行的时候就会寻找与super()所对应的构造方法而不会再去寻找父类的不带参数的构造方法。与this一样,super也必须要作为构造方法的第一条执行语句,前面不能有其他可执行语句。
11. 关于继承的3点:
a)父类有的,子类也有
b)父类没有的,子类可以增加
c)父类有的,子类可以改变
程序示例:
package com.zp.base;
public class Parent {
String name ="name = father";
public Parent(int a){
System.out.println("parent...");
}
public void say(){
System.out.println("I can say....");
}
}
package com.zp.base;
public class Child extends Parent {
String name ="name = son";
int age = 20;
public Child(){
super(1);
System.out.println("Child...");
}
public void study(){
System.out.println("the son is student....");
}
public static void main(String [] args){
Child c = newChild();
System.out.println(c.name);
System.out.println(c.age);
c.study();
c.say();
}
}
结果:parent...
Child...
name = son
20
the son is student....
I can say....
12. 关于继承的注意事项
a) 构造方法不能被继承
b) 方法和属性可以被继承
c) 子类的构造方法隐式地调用父类的不带参数的构造方法
d) 当父类没有不带参数的构造方法时,子类需要使用super来显式地调用父类的构造方法,super指的是对父类的引用 。
e) super关键字必须是构造方法中的第一行语句。
13. 方法重写(Override):又叫做覆写,子类与父类的方法返回类型一样、方法名称一样,参数一样,这样我们说子类与父类的方法构成了重写关系。
14. 方法重写与方法重载之间的关系:重载发生在同一个类内部的两个或多个方法。重写发生在父类与子类之间。
15. 当两个方法形成重写关系时,可以在子类方法中通过super.say()形式调用父类的say ()方法,其中super. say ()不必放在第一行语句,因此此时父类对象已经构造完毕,先调用父类的run()方法还是先调用子类的say ()方法是根据程序的逻辑决定的。
程序示例:
public class Parent {
String name = "name = father";
public Parent(int a){
System.out.println("parent...");
}
public void say(){
System.out.println("Parent can say....");
}
}
package com.sdjz;
public class Child extends Parent{
public Child(String name) {
super(1);
System.out.println("child....");
}
@Override
public void say() {
System.out.println("Child cansay....");
}
public void study(){
//super用来调用父类方法,可以放在任何位置,这也是和在构造器中的区别;
super.say();
System.out.println("the son isstudent....");
super.say();
}
public static void main(String[] args) {
Child child = new Child("zhangpeng");
child.say();
child.study();
}
}
结果:parent...
child....
Child can say....
Parent can say....
the son is student....
Parent can say....
分析:使用super调用父类方法的时候,如果没有重写父类方法,这时候就没有和父类方法重名的方法了,子类就可以直接调用父类的方法了,但是在子类重写父类方法的时候就只能用super调用父类方法了;
代码示例:(父类和上面代码一样)
public class Child extends Parent{
public Child(String name) {
super(1);
System.out.println("child....");
}
public void study(){
say();
}
public static void main(String[] args) {
Child child = new Child("zhangpeng");
child.study();
}
}
结果:parent...
child....
Parent can say....
16. 在定义一个类的时候,如果没有显式指定该类的父类,那么该类就会继承于java.lang.Object类(JDK提供的一个类,Object类是Java中所有类的直接或间接父类)。
17. 多态(Polymorphism):即多种形态;我们说子类就是父类(玫瑰是花,男人是人),因此多态的意思就是:父类型的引用可以指向子类的对象。
程序示例:
package com.zp.base;
public class Parent {
public void say(){
System.out.println("parent cansay....");
}
}
package com.zp.base;
public class Child extends Parent {
public void say(){
System.out.println("child cansay....");
}
public static void main(String [] args){
Parent c = new Child();//多态:父类应用指向子类对象
c.say();
}
}
18. Parent p = new Child();当使用多态方式调用方法时,首先检查父类中是否有say()方法,如果没有则编译错误;如果有,再去调用子类的say()方法,若子类没有则调用父类的say()方法,p是父类引用,所以是Parent类型的,只是指向了Child类型,只能调用父类有的方法。
19. 一共有两种类型的强制类型转换:
a) 向上类型转换(upcast):比如说将Cat类型转换为Animal类型,即将子类型转换为父类型。对于向上类型转换,不需要显式指定。
b) 向下类型转换(downcast):比如将Animal类型转换为Cat类型。即将父类型转换为子类型。对于向下类型转换,必须要显式指定(必须要使用强制类型转换)。
示例:Parent p = new Child(); //向上类型转换;
p= new Parent();
Child c = (Child)p; //向下类型转化;
20. 一个突出多态简便作用的例子:
package com.zp.duotai;
public class Car {
public void run(){
System.out.println("Car isruning....");
}
}
package com.zp.duotai;
public class QQ extends Car{
@Override
public void run() {
System.out.println("QQ isruning.....");
}
}
public class BMW extends Car{
@Override
public void run() {
System.out.println("BMW isruning.....");
}
}
public class Test {
public void run(Car car){//car的引用
car.run();
}
public static void main(String[] args) {
Test test = new Test();
Car car = new QQ();//多态,
test.run(car);
}
}