黑马程序员-java基础(四)-面向对象(封装、继承、多态)

来源:互联网 发布:sql trigger insert 编辑:程序博客网 时间:2024/05/17 09:06

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

面向对象(封装、继承、多态)

1  封装

封装:隐藏对象的属性和细节,仅对外提供公共的访问方式。

好处:

1、将变化隔离,提高安全性

2、便于使用,提高重用性

原则:

1、将不需要对外提供的内容都隐藏起来

2、把属性都隐藏,提供公共方法对其进行访问

public class Test {public static void main(String[] args) {Student t=new Student();//创建对象t.setAge(30);System.out.println("age="+t.getAge());t.setAge(140);}} class Student//创建student{//private:权限修饰符,私有,不希望别人直接访问赋值,通过私有化把属性进行隐藏private int age;//对外提供方法访问隐藏的属性public void setAge(int age){//进行条件限制if(0<age&&age<120)this.age=age;elseSystem.out.println("年龄设置错误");}//对外提供方法访问隐藏属性public int getAge(){return age;}}

运行结果:

ps.

1.封装不等于私有,私有仅仅是封装的一种表现形式

2.之所以对外提供访问方式,是通过在访问方式中加入逻辑判断语句,操作数据,提高代码的健壮性

【】单例设模式

设计模式:对问题行之有效的解决方式,是一种思想

单例设计模式:保证一个类在内存中对象的唯一性

PS.

当多个程序使用一个配置对象时就需要保证该对象的唯一性

确保对象唯一性思路(代码体现):

1、禁止其他程序new一个该类对象(将构造函数私有化

2、本类中自定义一个对象,让其他程序可以访问该类对象(在类中创建一个本类对象)

3、对外提供访问方式,让其他程序访问(提供方法获取该对象)

饿汉式

<span style="font-size:12px;">class Single{private int num;public void setNum(int num){this.num=num;}public int getNum(){return this.num;}private Single(){}//将构造函数私有化,其他程序无法创建该类对象static Single s=new Single();//创建一个本类对象//需要被其他程序调用,但是其他程序不能创建该类对象,所以要定义为静态用类名调用public static Single getInstance(){return s;}}public class Test {public static void main(String[] args) {Single s1=Single.getInstance();//通过类名调用静态方法Single s2=Single.getInstance();System.out.println(s1==s2);//验证对象的唯一性s1.setNum(10);System.out.println(s2.getNum());//验证对象的唯一性}}</span>

运行结果:


PS.
之所以不用Single.s;的方式获取Single对象,而采用getInstance获取是因为在getInstance方法中我们可以做一些判断来决定是否返回Single的对象,也就是实现了对单例对象的可控。所以,给Single的构造方法加上了private限制,禁止使用者直接采用Single.s;的方式获取。

<span style="font-size:12px;">class Single{private int num;public void setNum(int num){this.num=num;}public int getNum(){return this.num;}private Single(){}//将构造函数私有化,其他程序无法创建该类对象static Single s=null;//没有对象,调用getInstance时才创建对象//需要被其他程序调用,但是其他程序不能创建该类对象,所以要定义为静态用类名调用public static Single getInstance(){if(s==null)s=new Single();return s;}}public class Test {public static void main(String[] args) {Single s1=Single.getInstance();//通过类名调用静态方法Single s2=Single.getInstance();System.out.println(s1==s2);//验证对象的唯一性s1.setNum(10);System.out.println(s2.getNum());//验证对象的唯一性}}</span>

运行结果:

2  继承

继承:(extends 类名)

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。

1、提高代码的复用性

2、继承让类与类之间产生了关系,有了这个关系,才有多态的关系。

ps.

1、子类可以直接访问父类非私有的成员

2、子类无法继承父类私有的成员

<span style="font-size:12px;">class Person{int age=0;String name="baby";}class Student extends Person//Student继承Person类{public void study(){System.out.println(name+"...Student study....");//子类可以直接访问父类非私有成员}}public class Test {public static void main(String[] args) {Student s=new Student();s.age=3;System.out.println("name:"+s.name+"...age:"+s.age);s.study();}}</span>
运行结果

PS.

继承提高了代码的复用性

PS.

java只支持单继承,不支持多继承,但支持多实现

这是因为多继承存在安全隐患,当多个父类有相同的方法时,java不知道执行哪一个

PS.

1、使用继承体系,先查阅体系父类的描述,因为父类找那个定义的是该体系中共性功能

2、在具体调用时,要创建子类的对象,因为a.父类可能不能创建对象b.创建子类对象可以使用更多的功能,包括基本的也包括特有的。

归纳:查阅父类功能,创建子类使用功能

子父类中成员的特点

1、成员变量:子父类中类成员的特点:如果子类中出现非私有的同名成员变量时,访问本类变量用this,访问父类变量用super。

<span style="font-size:12px;">class Person{int age=5;}class Student extends Person//Student继承Person类{int age=7;public void show(){System.out.println(this.age+"..."+super.age);//子类可以直接访问父类非私有成员}}public class Test {public static void main(String[] args) {Student s=new Student();s.show();}}</span>

运行结果:7...3

2、成员函数:子类出现和父类一样的函数时,子类对象调用函数会运行子类函数内容覆盖父类函数内容(因此在定义新功能时可以使用覆盖,保留父类的功能定义并重写功能内容)

<span style="font-size:12px;">class Person{public void show(){System.out.println("person run...");}}class Student extends Person//Student继承Person类{public void show(){System.out.println("student run...");//子类重载父类的show方法}}public class Test {public static void main(String[] args) {Student s=new Student();s.show();//运行的是子类的重载后的show方法}}</span>

运行结果


PS.

 在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取

<span style="font-size:12px;">class Phone{    void call(){}    void show(){         System.out.println("number" );   }}class NewPhone extends Phone{    void show(){    super.show(); //获取父类被重载的方法    System.out.println("name" );    System.out.println("pic" );   }}public class Test {public static void main(String[] args) {NewPhone p = new NewPhone();        p.show();}}</span>



PS.

a、子类覆盖父类,必须保证子类权限大于父类权限

b、父类的private方法不能被覆盖

b、重载(同一个类中):只看同名函数的参数列表     重写(子类中重写父类):子父类方法要一模一样


3、构造函数:在子类的对象进行初始化时,父类构造函数也会运行,因为子类的构造函数默认第一行隐示语句super(),而且子类中所有的构造函数默认第一行都是super()

<span style="font-size:12px;">class Fu{    Fu()    {    System.out.println("fu run....");    }   }class Zi extends Fu{Zi()   {//super();子类的第一行默认有一个访问父类构造函数的隐式语句System.out.println("zi run....");    }  }public class Test {public static void main(String[] args) {Zi z = new Zi();}}</span>

运行结果:

那么为什么子类一定要访问父类的构造函数呢?

因为父类中的数据子类可以直接获取,所以子类对象建立时,需要先查看父类是如何对这些数据进行初始化的。

<span style="font-size:12px;">class Fu//父类没有空参数的构造函数</span><span style="font-size:12px;">{    Fu(int i)    {    System.out.println("fu run...."+i);    }   }class Zi extends Fu{Zi()   {         //super();子类的第一行默认有一个访问父类构造函数的隐式语句         //父类没有空参数的构造函数,因此此处要手动添加super(参数)来访问父类的指定构造函数         System.out.println("zi run....");    }  }public class Test {public static void main(String[] args) {Zi z = new Zi();}}</span>

运行结果:


PS.

1、当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。
2、子类构造函数中如果使用this调用了本类构造函数,那么默认的super();就没有了,因为super和this都只能定义在第一行,所以只能有一个。但是可以保证的是,子类中肯定会有其他的构造函数访问父类的构造函数。
3、super语句必须要定义在子类构造函数的第一行!因为父类的初始化动作要先完成。

对象的实例化过程

<span style="font-size:12px;">class Fu{    Fu(){        super();        //调用的是子类的show方法,此时其成员变量num还未进行显示初始化        show();        return; } void show(){       System.out.println("fu show" ); }}class Zi extends Fu{  int num = 8;  Zi(){        super();        //通过super初始化父类内容时,子类的成员变量并未显示初始化,等super()父类初始化完毕后,才进行子类的成员变量显示初始化        return;  }  void show(){  System.out.println("zi show..." + num);  }}class Test{  public static void main(String[] args){       Zi z = new Zi();       z.show();  }}</span>

PS.

一个对象实例化过程,以Person p = new Person();为例:
1. JVM会读取指定的路径下的Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接的父类的情况下)。
2. 在内存中开辟空间,并分配地址。
3. 并在对象空间中,对对象的属性进行默认初始化。
4. 调用对应的构造函数进行初始化。
5. 在构造函数中,第一行会先到调用父类中构造函数进行初始化。
6. 父类初始化完毕后,再对子类的属性进行显示初始化。
7. 再进行子类构造函数的特定初始化。
8. 初始化完毕后,将地址值赋值给引用变量。


【】final关键字:

1、修饰符,可以修饰类、函数、变量

2、被final修饰的类不可以被继承。(final class 类名)

3、被final修饰的方法不可以复写。(final 函数名)

4、可以修饰成员变量,也可以修饰局部变量,被final修饰的变量是一个常量只能被赋值一次

5、 写法规范:常量所有字母都大写,多个单词,中间用_连接。


【】抽象类:abstract(把多个事物的共性和本质的内容抽取出来)

Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。

抽象类特点:(和一般类无大区别,只是不确定部分需要子类明确)

1、抽象方法一定在抽象类中(格式:修饰符 abstract 返回值类型 函数名(参数列表) ;

2、抽象方法和抽象类都必须被abstract关键字修饰

3、抽象类不可以用new创建对象,因为调用抽象方法没有意义

4、抽象类中的方法要被使用,必须由子类复写所有的抽象方法,建立子类对象调用(因此抽象类一定是父类)

PS.

抽象类和一般类的区别

A. 一般类有足够的信息描述事物。
        抽象类描述事物的信息有可能不足。
B. 一般类中不能定义抽象方法,只能定义非抽象方法。
       抽象类中可定义抽象方法,同时也可以定义非抽象方法。
C. 一般类可以被实例化。
       抽象类不可以被实例化

PS.

抽象类可以不用定义抽象方法,仅仅是不让该类创建对象

<span style="font-size:12px;">/** *  需求:   公司中程序员有姓名,工号,薪水,工作内容。   项目经理除了有姓名,工号,薪水,还有奖金,工作内容。    分析:    在这个问题领域中,通过名词提炼法:    程序员:    属性:姓名,工号,薪水。    行为:工作。    经理:    属性:姓名,工号,薪水,奖金。    行为:工作。    程序员和经理不存在着直接继承关系。    但是,程序员和经理却具有共性内容,可以进行抽取,因为他们都是公司的雇员。    可以将程序员和经理进行抽取,建立体系。  * */abstract class Employee{private String name,num;    private int pay;Employee(String name,String num,int pay){this.name=name;this.num=num;this.pay=pay;}abstract void work();}class Programmer extends Employee{Programmer(String name,String num,int pay){super(name,num,pay);}void work(){        System.out.println("程序员工作。。。");  }}class Manager extends Employee{Manager(String name,String num,int pay){super(name,num,pay);}public void work(){System.out.println("经理工作。。。");}</span><span style="font-size:12px;">}</span>


【】接口:(ingterface 类名)

特殊的抽象类,当抽象类中的方法都是抽象的,那么该类可以通过接口的形式表示,其中的常量和修饰符有固定的修饰符

常量:public static fin   

方法:public abstract

interface inter{}interface interA{}class demo{}class test extends demo implements inter,interA{}//多实现加继承,接口之间可以多继承。
接口特点:

a、接口是对外暴露的规则

b、接口是程序的功能扩展

c、接口可以用来多实现

d、类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口

e、接口与接口之间可以有继承关系。(继承和接口近似于属于的基本功能和扩展的功能区别,继承是 is a接口是like a)

P.S.
    a、虽然抽象类中的全局变量和抽象方法的修饰符都可以不用写,但是这样阅读性很差。所以,最好写上。
    b、类与类之间是继承关系,类与接口直接是实现关系。
    c、接口不可以实例化,能由实现了接口并覆盖了接口中所有的抽象方法的子类实例化。否则,这个子类就是一个抽象类。

<span style="font-size:12px;">interface CC{       void show();}interface MM{       void method();}//接口与接口之间是继承关系,而且接口可以多继承interface QQ extends CC,MM{       public void function();}class WW implements QQ{       //覆盖3个方法       public void show(){}       public void method(){}       public void function(){}} </span>

抽象类和接口的异同点?

相同点:

都是不断向上抽取而来的。

不同点:

a、 抽象类需要被继承,而且只能单继承。
接口需要被实现,而且可以多实现。
b、抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法。
接口中只能定义抽象方法,必须由子类去实现。
c、 抽象类的继承,是is a关系,定义该体系的基本共性内容。接口的实现是like a关系。

3  多态

多态:事物存在的多种体现形态 animal c=new cat()//类型提升,向上转型>>>cat c=(cat)a//强制将父类的引用转成子类类型。

多态的体现:父类的引用指向了自己的子类对象,接受自己的子类对象(Animal a=new cat())

多态的前提:必须是类和类之间的关系,要么继承要么实现

好处:提高了代码的扩展性,前期定义的代码可以使用后期的内容。
弊端:前期定义的内容不能使用(调用)后期子类的特有内容。

<span style="font-size:12px;">abstract class  Animal{    abstract void eat();}class Cat extends Animal{    void eat(){    System.out.println("猫吃鱼");    }void catchMouse(){System.out.println("猫抓老鼠");};}class Dog extends Animal{    void eat(){    System.out.println("狗吃骨头");    }void look(){System.out.println("狗看家");};}class Pig extends Animal{    void eat(){    System.out.println("猪吃菜");    }void sleep(){System.out.println("猪睡觉");};}class Test{public static void main(String[] args) {function(new Cat());function(new Dog());function(new Pig());}public static void function(Animal a){a.eat();}}</span>

运行结果:

<span style="font-size:12px;">abstract class  Animal{    abstract void eat();}class Cat extends Animal{    void eat(){    System.out.println("猫吃鱼");    }void catchMouse(){System.out.println("猫抓老鼠");};}class Dog extends Animal{    void eat(){    System.out.println("狗吃骨头");    }void look(){System.out.println("狗看家");};}class Pig extends Animal{    void eat(){    System.out.println("猪吃菜");    }void sleep(){System.out.println("猪睡觉");};}class Test{public static void main(String[] args) {function(new Cat());function(new Dog());function(new Pig());}public static void function(Animal a){<span style="font-family:微软雅黑;">//Animal a=new Cat()父类的引用指向子类对象,向上转型</span>a.eat();<span style="font-family:微软雅黑;">//直接定义子类的特殊方法a.catchMouse会报错,因为Animal类中没有catchMouse方法</span>if(a instanceof Cat)<span style="font-family:微软雅黑;">//instanceof-关键字(对象是否属于指定的类型)</span>{Cat c=(Cat) a;<span style="font-family:微软雅黑;">//向下强转型,始终都是子类类型在做变化</span>c.catchMouse();<span style="font-family:微软雅黑;">//直接</span>}else if(a instanceof Dog){((Dog) a).look();}else if(a instanceof Pig){((Pig) a).sleep();}}}</span>

运行结果:


多态中成员变量的特点

       编译时:参考引用型变量所属的类中是否有调用的成员变量。有,编译通过,没有,编译失败。
       运行时:参考引用型变量所属的类中是否有调用的成员变量,并运行该所属类中的成员变量。
       简单说:编译和运行都参考等号的左边

<span style="font-size:12px;">class Fu{       int num = 3;}class Zi extends Fu{       int num = 4;}class DuoTaiDemo{       public static void main(String[] args){            Zi f1 = new Zi();            System.out.println(f1.num);<span style="font-family:微软雅黑;">//调用的子类的成员变量</span>            Fu f2 = new Zi();            System.out.println(f2.num);<span style="font-family:微软雅黑;">//调用的是父类的成员变量</span>       } }</span>

运行结果:4  3

多态中成员函数的特点:(非静态)

       编译时:参考引用型变量所属的类中是否有调用的函数。有,编译通过。没有,编译失败。
       运行时:参考的是对象所属的类中是否有调用的函数。
       简单说:编译看左边,运行看右边。

<span style="font-size:12px;">class Fu{    void show(){         System.out.println("fu show");    }}class Zi extends Fu{    void show(){         System.out.println("zi show");    }}class Test{public static void main(String[] args) {Fu c=new Zi();c.show();}}</span>

运行结果:


多态中成员函数的特点:(静态)

       编译时:参考的是对象所属的类中是否有调用的函数。
       运行时:参考的是对象所属的类中是否有调用的函数。
       简单说:编译和运行看左边。

class Fu{    static void method(){         System.out.println("fu static method");    }}class Zi extends Fu{    static void method(){         System.out.println("zi static method");    }}class Test{public static void main(String[] args) {Fu c=new Zi();c.method();//这里调用的是父类的静态方法Fu.method();//静态方法是类方法,对象还没有时就已经存在于方法区}}

运行结果:


4  内部类

内部类:将一个类定义在另一个类的里面,里面那个类就称为内部类(内置类,嵌套类)

访问特点:

a、内部类可以直接访问外部类的成员(包括私有)

b、外部类访问内部类成员必须创建内部类对象

<span style="font-size:12px;">class Outer{    private int num = 3;    class Inner    {          void show(){               System.out.println("Inner run..." + num);          }    }}class Test{    public static void main(String[] args){    //直接访问外部类中的内部类中的成员    Outer.Inner in = new Outer().new Inner();    in.show();    }}</span>

P.S.

1、内部类定义在成员位置上,可以被private、static成员修饰符修饰。被static修饰的内部类只能访问外部类中的静态成员。

2、静态的内部类,内部类成员也是静态的,可以不用创建内部类对象,直接调用。

<span style="font-size:12px;">class Outer{    private static int num = 3;    static class Inner    {     static void show(){               System.out.println("Inner run..." + num);          }    }}class Test{    public static void main(String[] args){    //如果内部类是静态的,相当于一个外部类    Outer.Inner in = new Outer.Inner();    in.show();    Outer.Inner.show();//内部类为静态的可以直接通过类名访问    }}</span>

P.S. 

如果内部类中定义了静态成员,该内部类也必须是静态的!

<span style="font-size:12px;">class Outer{    int num = 3;    class Inner{          int num = 4;          void show(){                int num = 5;                System.out.println(num);                System.out.println(this.num);                System.out.println(Outer.this.num);          }    }    void method(){          new Inner().show();    }}class Test{    public static void main(String[] args){          new Outer().method();    }}</span>

运行结果 :5 4 3

内部类定义在局部位置上,也可以直接访问外部类中的成员;同时可以访问所在局部中的局部变量,但必须是被final修饰的。

<span style="font-size:12px;">class Outer{       int num = 3;       void method(final int y){             final int x = 9;             class Inner{                   void show(){                        System.out.println("show..." + x + "," + y);                   }             }            Inner in = new Inner();            in.show();       } }class Test{       public static void main(String[] args){             new Outer().method(4);       }}</span>

【注意】内部类定义了静态变量,内部类必须是静态的。

【注意】静态是成员修饰符,不能修饰局部变量。

内部类定义在局部

1、不可以被成员修饰符修饰如:static

2、可以直接访问外部类中的成员,因为还持有外部类中的引用,但是不可以访问他所在的局部中的变量,只能访问被final修饰的局部变量

【】匿名内部类

定义匿名内部类前提:
       内部类必须继承一个类或者实现接口

abstract class Demo//匿名内部类需要继承一个类或者实现接口{abstract void show();}class Outer{    int num = 3;    /*class Inner extends Demo//一般定义内部类方法    {    void show()    {    System.out.println("show....."+Outer.this.num);    }    }*/    void method(){        //new Inner().show()    //匿名内部类,将内部类(注释掉的代码块)简写    new Demo()        {        void show()        {        System.out.println("show....."+Outer.this.num);        }        }.show();    }}class Test{    public static void main(String[] args){          new Outer().method();    }}

内部类格式:new  父类或者接口(){定义子类的功能}------------(实质为一个匿名子类对象 ,因此上面的方法只能对方法也只能调用一次)

abstract class Demo//匿名内部类需要继承一个类或者实现接口{abstract void show1();abstract void show2();}class Outer{    int num = 3;    /*class Inner extends Demo    {    void show()    {    System.out.println("show....."+Outer.this.num);    }    }*/    void method(){        //new Inner().show()    //匿名内部类,将内部类(注释掉的代码块)简写    Demo d=new Demo()//当父类有多个方法时可以定义一个父类引用指向子类对象,实现多个方法调用        {        void show1()        {        System.out.println("show.....1..."+Outer.this.num);        }        void show2()        {        System.out.println("show.....2..."+Outer.this.num);        }        void show3()        {        System.out.println("show.....3..."+Outer.this.num);        }        };    d.show1();    d.show2();    //d.show3();会报错,不能调用匿名对象的特有方法,因为父类中没有定义此方法无法调用        }}class Test{    public static void main(String[] args){          new Outer().method();    }}

运行结果


【例】

interface Inter//匿名内部类需要继承一个类或者实现接口{void method();}class Outer{    int num = 3;    /*static class Inner implements  Inter    {    public void method()    {    System.out.println("method run");    }    }*/           public static Inter function()        {        //return new Inner();        return new Inter()//匿名内部类        {        public void method()            {            System.out.println("method run");            }                };        }    }class Test{    public static void main(String[] args){    //Outer.function()  说明Outer中有一个静态方法是function    //.method();  function的返回值是对象,而且是Inter类型的对象    Outer.function().method();    }}
【例】

interface Inter{       void show1();       void show2();}/*通常的使用场景之一:当函数参数是接口类型时,而且接口中的方法不超过三个。可以用匿名内部类作为实际参数进行传递。*/class InnerClassDemo{      public static void main(String[] args){             show(new Inter(){                   public void show1(){                        System.out.println("...show1..." );                   }                   public void show2(){                        System.out.println("...show2..." );                   }            });       }       public static void show(Inter in){            in.show1();            in.show2();       }}
【例】

class Test{    public static void main(String[] args){    new Object()//创建Object的子类匿名对象    {    void show()    {    System.out.println("object run");    }        }.show();    }}


0 0