面向对象(二)



一、单例设计模式


1、什么是单例设计模式?
    对于单例模式(Singleton Pattern)是一个比较简单的模式,他的定义如下:
        Ensure a class has only one instance,and provide a global point of access to it.
        意思是确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
        好处:保证对象的唯一性
        使用场景:比如多个程序都要使用一个配置文件中的数据,而且要实现数据共享和交换。必须要将多个数据封装到一个对象中。而且多个程序操作的是同一个对象。那也就是说必须保证这个配置文件对象的唯一性。

        如何能保证对象的唯一性?
            一个类只要提供了构造方法,就可以产生多个对象。完全无法保证唯一。既然数量不可    控,干脆不让其他程序建立对象。

        不让其他程序创建,对象何在?
            自己在本类中创建一个对象,好处,对象可控。

        创建完成后,是不是要给其他程序提供访问的方式?
            怎么实现这个步骤?
                    怎么就能不让其他程序创建对象呢?
                        直接私有化构造方法,不让其他程序创建的对象存在。
                    直接在本类中new一个本类对象
                    定义一个功能,其他程序可以通过这个功能获取到本类对象。
        

2、单例模式分成两种:懒汉式、饿汉式

3、下面就通过代码来看看这三种单例模式的区别
饿汉式:

116//饿汉式  
117class Singleton_1{  
118    private static Singleton_1 s = new Singleton_1();  
119    private Singleton_1(){}  
120    public static Singleton_1 getInstance(){  
121        return s;  
122    }  
123      
124}  

懒汉式:(容易引发线程安全问题)

125//懒汉式(单例模式的延迟加载方式),面试最多的是懒汉式  
126class Singleton_2{  
127    private static Singleton_2 s = null;  
128    private Singleton_2(){}  
129    public static Singleton_2 getInstance(){  
130        if(s == null){  
131            s = new Singleton_2();  
132        }  
133        return s;  
134    }  
135}  

二、继承

1、假设有两个类,一个类是Student类,另外一类是Worker类,两个类中都有属性nameage,那么既然有共同的属性,我们能不能对他们进行抽取呢?
          答案:可以,可以将两个类中的name属性和age属性都抽取取来,放到另外一个类,这个类就是Person,因为学生是人,工人也是人,所以就抽取出来再建立Person类。
继承的关键字:extends

代码体现:

136package day08.itcast01;  
137public class ExtendsDemo {  
138    public static void main(String[] args) {  
139        Student s = new Student();  
140        s.name = "林青霞";  
141        s.age = 16;  
142        s.show();  
143          
144        Worker w = new Worker();  
145        w.name = "小二";  
146        w.age = 28;  
147        w.show();  
148    }  
149}  
150class Student extends Person{//定义一个Student类,并提供一个成员方法  
151    public void study(){  
152        System.out.println("我在学习");  
153    }  
154}  
155class Worker extends Person{  //定义一个Worker类,并提供一个成员方法  
156    public void work(){  
157        System.out.println("我在工作");  
158    }  
159}  
160class Person{   //定义一个Person类,由Student类和Worker类抽取而来,是Worker类和Student类的父类(基类、超类)  
161    String name;  
162    int age;  
163    public void show(){  
164        System.out.println(name+"---"+age);  
165    }  
166}  

继承的好处:提高了代码的复用性,为面向对象另一个特征多态提供了前提条件

什么时候定义继承?
必须保证类与类之间的所属(is a)关系,XXXXX的一种
比如:狗是动物的一种,学生是人的一种

Java当中允许单继承,不允许多继承
单继承:一个子类只会有一个父类
多继承:一个子类会有多个父类

2、继承中子父类成员的特点:
    1、成员变量
            特殊情况:当子类和父类定义了同名的成员变量的时候,如何在子类中访问父类中的变量?
            通过关键字super来完成
            super的用法与this的用法类似,
                this代表的是本类对象的引用。
                super代表的是父类的内存空间
    2、成员方法
            当子类和父类定义了一模一样的的方法时,当子类调用该方法时,运行的是子类中的方法
            这种情况在子父类中被称之为方法的重写。
            何时需要重写方法?
                当子类的方法有自己的特有的功能的时候就需要重写。
        子类重写父类的方法必须保证权限要大于或者等于父类的权限
        静态只能覆盖静态的
        写法上需要注意的:必须一模一样,方法的返回值类型 方法名 参数列表都要一样。
代码体现:

167package day09.itcast01;  
168public class ExtendsDemo1 {  
169    public static void main(String[] args) {  
170        Zi zi = new Zi();  
171        zi.age = 15;  
172        zi.des();  
173    }  
174}  
175class Fu{  
176    int age;  
177    public void des(){  
178        System.out.println("父类的des方法"+"---父类的成员变量---"+age);  
179    }  
180}  
181class Zi extends Fu{  
182    int age;  
183    public void des(){  
184        super.age = 10;  
185        super.des();  
186        System.out.println("子类的des方法"+"---子类的成员变量---"+age);  
187    }  
188      
} 

3、子父类中构造方法的的特点

190package day09.itcast02;  
191public class ExtendsDemo2 {  
192    public static void main(String[] args) {  
193        Son son = new Son();  
194    }  
195}  
196class Father{  
197    Father(){  
198        System.out.println("Father is running");  
199    }  
200}  
201class Son extends Father{  
202    Son(){  
203        System.out.println("Son is running");  
204    }  
} 

分析运行结果:
因为在子类的所有构造方法中的第一行都默认有一个super();它会调用父类的构造方法
 为什么会有super();呢?
因为在子类进行初始化的时候,先要对父类进行初始化,只有对父类进行初始化完成后,才能使用父类的一些方法

当父类没有空参的构造方法时,需要使用super关键字去调用相应的构造方法

如果在子类的第一行使用了this调用本类的其他构造方法,还会有super();吗?
没有,因为this()或者super()只能定义在构造方法的第一行

父类的构造方法中是否有super();
有,因为所有类的构造方法的第一行都有一个super();此时父类调用的是所有类的父类Object类。

如果默认的隐式super语句没有对应的构造函数,必须在构造函数中通过this或者super的形式明确调用的构造函数。

三、final关键字

继承的缺点:打破封装性,如何能保证继有继承又不会打破封装性呢?
就不让其他类继承该类,就不会重写方法。这时就需要用到final关键字
final的意思是最终,它用于修饰类,方法或者变量(成员变量、局部变量、静态变量)

final的特点:
1final修饰的类是一个最终类,不能再派生子类
    如果一个类中的方法部分需要重写,部分不需要,就对不需要被重写的方法使用final修饰
2final修饰的方法是最终方法,该方法不能被重写
3final修饰的变量是一个常量,只能被赋值一次

什么时候需要在程序中定义final常量呢?
当程序中一个数据使用时是固定不变的,这时为了增加阅读性,可以给该数据起个名字。
这就是变量,为了保证这个变量的值不被修改,加上final修饰,这就是一个阅读性很强的常量。
书写规范:被final修饰的常量名所有的字母都是大写,如果该变量名是由多个单词组成的,每个字母都大写,并且单词之间使用"_"连接。



四、抽象类

对与狗和狼这两种动物他们都有一个吼叫的行为,而且他们还属于动物,对他们的共性进行向上抽取,可以使用继承,但是狗和狼吼叫的行为又不同。这时使用继承就显得不合适了,这时就需要使用另外一个关键字abstract(抽象的)对父类进行修饰。

抽象类的特点:抽象类和抽象方法都需要使用abstract修饰,抽象方法一定要定义抽象类中,抽象类中的方法不一定都是抽象方法。

只有覆盖了抽象类中的所有抽象方法后,其子类才可以实例化。否则该子类还是一个抽象类。

抽象类要实例化的话需要通过子类对父类进行实例化。(多态)

细节:
抽象类一定是一个父类
是的,因为抽象类就是子类的功能不断抽取出来的

抽象类中是否有构造方法?
有,不能给自己的对象实例化,可以给子类的对象进行初始化。

抽象类和普通类的异同点?
相同:它们都是用来描述事物的,它们之间都可以定义属性和行为

不同:一般类可以具体的描述事物,抽象类描述的事物信息不具体
抽象类可以多定义一个成员:抽象方法。
一般类可以创建对象,而抽象类不能创建对象。

抽象类中是否可以定义普通方法?
可以,如果抽象类中定义了普通方法,那么其抽象类的作用就是不能对该类进行实例化。

抽象关键字abstract不能与哪些关键字共存?
final
private
static

代码体现:

206package day09;  
207public class AbstractDemo {  
208    public static void main(String[] args) {  
209        Dog d = new Dog();  
210        d.show();  
211        d.speak();  
212          
213    }  
214}  
215abstract class Animal{  
216    public void show(){  
217        System.out.println("Animal");  
218    }  
219    public abstract void speak();  
220}  
221class Dog extends Animal{  
222    public void speak(){  
223        System.out.println("小狗叫");  
224    }  
225}  

案例:
需求:公司中程序员有姓名,工号,薪水,工作内容。
项目经理除了有姓名,工号,薪水,还有奖金,工作内容。
分析:程序员和项目经历都属于公司里的员工,而且他们都有共性:姓名、工号、薪水以及工作内容

226package day09.itcast02;  
227public class AbstractDemo {  
228    public static void main(String[] args) {  
229        Programer p = new Programer("张三","448",5000);  
230        Manager m = new Manager("李四","500",5000,2500);  
231        p.content();  
232        p.show();  
233        m.content();  
234        m.show();  
235          
236    }  
237}  
238abstract class Employee{  
239    String name;  
240    String number;  
241    int salary;  
242    public Employee(String name,String number,int salary){  
243        this.name = name;  
244        this.number = number;  
245        this.salary = salary;  
246    }  
247    public abstract void content();  
248    public abstract void show();  
249      
250}  
251class Programer extends Employee{  
252    public Programer(String name,String number,int salary){  
253        super(name,number,salary);  
254    }  
255    public void content(){  
256        System.out.println("敲代码");  
257    }  
258    public void show(){  
259        System.out.println(name+"---"+number+"---"+salary);  
260    }  
261}  
262class Manager extends Employee{  
263    int pay;//奖金  
264    public Manager(String name,String number,int salary,int pay){  
265        super(name,number,salary);  
266        this.pay = pay;  
267    }  
268    public void content(){  
269        System.out.println("写规划");  
270    }  
271    public void show(){  
272        System.out.println(name+"---"+number+"---"+salary+"---"+pay);  
273    }  
274      
275}  

五、接口

当一个抽象类中的方法都是抽象方法的时候,这个抽象类就有另外一个表现方式,叫做接口(interface
定义接口使用关键字interface,格式:interface 接口名{}

接口中的成员已经被限定为固定的几种。
接口中的定义格式(两种)
    1、定义变量,但是变量必须有固定的修饰,public static final,所以接口中的变量也被称之为常量。
    2、定义方法,方法也有固定的修饰符,public abstract
        接口中的成员都是公共的。

特点:
接口不可以创建对象
子类必须覆盖掉接口中的所有抽象方法后,子类才可以被实例化。否则子类是一个抽象类。

定义接口的子类,类与类之间的关系是继承,而子类与接口之间的关系是实现(implements)
格式:
interface Animal{  // 定义一个动物接口
}
class Dog implements Animal{ //定义犬类实现了动物的接口
}

接口解决了多继承中调用不明确的弊端,讲多继承机制在java中通过多实现完成了
    
接口的出现避免了单继承的局限性
父类中定义事物的基本功能。
接口中定义事物的扩张功能。

类与类之间是继承(is a)关系,类与接口之间是实现(like a)关系.

接口与接口之间是继承关系,而且可以多继承。

抽象类和接口的区别:
接口中定义的方法都是抽象方法,不允许定义普通方法。
抽象类中可以允许有普通方法,但是抽象方法一定定义在抽象类中。

接口的思想:
1、对功能实现了扩展。
2、定义了规则。
3、降低了耦合性。

三、多态

什么是多态?
举例子:学生和工人,但是他们都是人中的某一类,这就是多态。多态就是一种事物的不同体现。
多态的体现:
父类的引用或者接口的引用指向了自己的子类对象。
好处:提高了程序的扩展性。
弊端:通过父类的引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法
多态的前提:继承或者实现。
多态通常都有重写操作。
代码体现:

276package day10.itcast01;  
277public class DuotaiDemo1 {  
278    public static void main(String[] args) {  
279        Person p = new Student();  
280        p.eat(p);  
281        Person p1 = new Worker();  
282        p1.eat(p1);  
283          
284    }  
285}  
286abstract class Person{  
287    public void eat(Person p){  
288        if(p instanceof Student ){  
289            System.out.println("吃饭");  
290            p.method();  
291        }else if( p instanceof Worker){  
292            System.out.println("吃饭");  
293            p.method();  
294        }  
295    }  
296    public abstract void method();  
297}  
298class Student extends Person{  
299    public void method(){  
300        System.out.println("学习");  
301    }  
302}  
303class Worker extends Person{  
304    public void method(){  
305        System.out.println("工作");  
306    }  
307}  

2、多态调用子类的特有方法

Person p = new Studnet();
或者Person p = new Worker();
父类引用指向了子类的对象,这是让子类对象进行类型的提升(向上转型)
好处:提高了扩展性,隐藏了子类,弊端:不能使用子类的特有方法。

如果想要使用子类的特有方法,只有子类的对象才能使用,这时就需要使用向下转型。
向下转型属于强制类型转换,向上转型是自动类型转换。

308package day10.itcast01;  
309public class DuotaiDemo1 {  
310    public static void main(String[] args) {  
311        Person p = new Student();  
312        p.eat(p);  
313//        p.write();  报错  
314        ((Student)p).writer();  
315        Person p1 = new Worker();  
316        p1.eat(p1);  
317        ((Worker)p1).work();  
318          
319    }  
320}  
321abstract class Person{  
322    public void eat(Person p){  
323        if(p instanceof Student ){  
324            System.out.println("吃饭");  
325            p.method();  
326        }else if( p instanceof Worker){  
327            System.out.println("吃饭");  
328            p.method();  
329        }  
330    }  
331    public abstract void method();  
332}  
333class Student extends Person{  
334    public void method(){  
335        System.out.println("学习");  
336    }  
337    public void writer(){  
338        System.out.println("学生的特有方法:写作业");  
339    }  
340}  
341class Worker extends Person{  
342    public void method(){  
343        System.out.println("工作");  
344    }  
345    public void work(){  
346        System.out.println("工人的特有方法:干活");  
347    }  
}  

注意:无论是向上转型还是向下转型,最终都是子类对象做着类型的变化。
【向下转型的注意事项】:
Person p = new Studnet();
Worker w = (Worker)p;
以上代码是不允许出现的,会导致ClassCastException异常
为了避免出现类转换异常,在进行类转换时常使用instanceof关键字对对象进行类型判断
格式:对象名 instanceof 类名

3、子父类成员的调用问题
        1、成员变量:当子父类中出现了同名的成员变量时
                编译时期:参考的是引用型变量所属的类是否有被调用的成员变量,没有,编译失败。   
                运行时期:参考的是引用型变量所属类中的成员变量。
                口诀:编译运行看左边。           
        2、成员方法:当子父类中出现了同名的成员方法时
                编译时期:参考左边,如果没有,编译失败
                运行时期:参考右边
                口诀:编译看左边,运行看右边。
                对于成员方法是动态绑定到对象上。
        3、静态方法
                编译和运行都参考左边
                静态方法是静态绑定到类上。

4、题目
看一下代码分析打印结果

349package day10.itcast02;  
350public class DuoTaiTest3  
351{  
352    public static void main(String[] args)  
353    {  
354        Fu f = new Zi();  
355        System.out.println("main :" +f.getNum());  
356    }  
357}  
358class Fu  
359{  
360    int x = 4;  
361    Fu()  
362    {  
363        System.out.println("A fu() : "+getNum());  
364    }  
365    int getNum()  
366    {  
367        System.out.println("B fu getnum run...."+x);  
368        return 100;  
369    }  
370}  
371class Zi extends Fu  
372{  
373    int x = 5;  
374    Zi()  
375    {    //super();默认存在并调用父类的无参构造  
376        System.out.println("C zi() : "+getNum());  
377    }  
378    int getNum()  
379    {  
380        System.out.println("D zi getnum run...."+x);  
381        return 200;  
382    }  
383}  

结果分析:
分析这道题首先从main方法开始,Fu f = new Zi(); 程序首先会调用子类的构造方法,进入到子类的构造方法时,因为在子类的构造方法中第一行都会有默认的super();调用父类的构造方法,所以程序进入到 父类的构造方法之中,在父类的构造方法中,有一条输出语句,输出语句中调用了getNum()方法,那么这时候调用的是哪个呢?因为在main方法中,使 用了多态,使父类的对象引用指向了子类,所以调用的是子类的getNum方法,在getNum方法中输出了一个x,因为从程序开始一直到现在都还未对x进 行显示的初始化,所以x的值为0,所以最想打印的是D zi getnum run....0,接着getNum方法返回了200到父类的构造方法中,所以接着打印了A fu() : 200,当打印了两条语句之后,又回到了子类的构造方法中,这时子类的构造方法中同样有一条输出语句,在语句中调用了getNum()方法,这个 getNum方法同样是子类中的方法,因为此时对子类中的x已经完成了显式初始化,所以打印了D zi getnum run....5,打印完成后又回到了子类的构造方法中执行了输出语句,此时打印了C zi() : 200,至此Fu f = new Zi()完成了所有的操作,最后mian方法中的输出语句中同样调用的是子类中的getNum()方法,所以打印的是main :200
综合以上的分析,可以得出最后的结果为:
D zi getnum run....0
A fu() : 200
D zi getnum run....5
C zi() : 200
main :200

 六、object

object是所有的类的父类或者间接父类
object类中有两个常用的方法
equlastoString方法
equals:通常用来比较两象是个对否相等。
toString:返回对象的字符串表现形式,Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于:
getClass().getName() + '@' + Integer.toHexString(hashCode())

一般来说equalstoString方法都需要被重写
案例:

384package cn.test;  
385public class EqualsDemo {  
386    public static void main(String[] args) {  
387        Student s1 = new Student("谢群",18);  
388        Student s2 = new Student("谢群",22);  
389        boolean  result = s1.equals(s2);  
390        System.out.println("result:"+result);  
391        System.out.println(s2.toString());  
392        s2.setAge(18);  
393        result = s1.equals(s2);  
394        System.out.println("result:"+result);  
395        System.out.println(s2.toString());  
396          
397    }  
398}  
399class Student{  
400    private String name;  
401    private int age;  
402    public Student(String name, int age){  
403        this.name = name;  
404        this.age = age;  
405    }  
406    public Student(){}  
407    public void setName(String name){  
408        this.name = name;  
409    }  
410    public String getName(){  
411        return name;  
412    }  
413    public void setAge(int age){  
414        this.age = age;  
415    }  
416    public int getAge(){  
417        return age;  
418    }  
419    public boolean equals(Object s){  
420        if(s == null){  
421            return false;  
422        }  
423        if(!(s instanceof Student)){  
424            return false;  
425        }  
426        if(s == this){  
427            return true;  
428        }  
429        Student s1 = (Student)s;  
430        return this.name.equals(s1.name) && this.age == s1.age;  
431    }  
432    public String toString(){  
433        return this.name +"----"+this.age;  
434    }  
435      
436      
437}  












你可能感兴趣的:(面向对象二)