当前位置:   article > 正文

黑马程序员_面向对象(三)

a. abstract class person { void show(); } class student extends person{
------- android培训java培训、期待与您交流! ----------


一、单例设计模式


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

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

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

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


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

3、下面就通过代码来看看这三种单例模式的区别
饿汉式:
  1. //饿汉式
  2. class Singleton_1{
  3. private static Singleton_1 s = new Singleton_1();
  4. private Singleton_1(){}
  5. public static Singleton_1 getInstance(){
  6. return s;
  7. }
  8. }


懒汉式:(容易引发线程安全问题)
  1. //懒汉式(单例模式的延迟加载方式),面试最多的是懒汉式
  2. class Singleton_2{
  3. private static Singleton_2 s = null;
  4. private Singleton_2(){}
  5. public static Singleton_2 getInstance(){
  6. if(s == null){
  7. s = new Singleton_2();
  8. }
  9. return s;
  10. }
  11. }


二、继承

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

代码体现:
  1. package day08.itcast01;
  2. public class ExtendsDemo {
  3. public static void main(String[] args) {
  4. Student s = new Student();
  5. s.name = "林青霞";
  6. s.age = 16;
  7. s.show();
  8. Worker w = new Worker();
  9. w.name = "小二";
  10. w.age = 28;
  11. w.show();
  12. }
  13. }
  14. class Student extends Person{//定义一个Student类,并提供一个成员方法
  15. public void study(){
  16. System.out.println("我在学习");
  17. }
  18. }
  19. class Worker extends Person{ //定义一个Worker类,并提供一个成员方法
  20. public void work(){
  21. System.out.println("我在工作");
  22. }
  23. }
  24. class Person{ //定义一个Person类,由Student类和Worker类抽取而来,是Worker类和Student类的父类(基类、超类)
  25. String name;
  26. int age;
  27. public void show(){
  28. System.out.println(name+"---"+age);
  29. }
  30. }

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

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

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

2、继承中子父类成员的特点:
1、成员变量
特殊情况:当子类和父类定义了同名的成员变量的时候,如何在子类中访问父类中的变量?
通过关键字super来完成
super的用法与this的用法类似,
this代表的是本类对象的引用。
super代表的是父类的内存空间
2、成员方法
当子类和父类定义了一模一样的的方法时,当子类调用该方法时,运行的是子类中的方法
这种情况在子父类中被称之为方法的重写。
何时需要重写方法?
当子类的方法有自己的特有的功能的时候就需要重写。
子类重写父类的方法必须保证权限要大于或者等于父类的权限
静态只能覆盖静态的
写法上需要注意的:必须一模一样,方法的返回值类型 方法名 参数列表都要一样。
代码体现:
  1. package day09.itcast01;
  2. public class ExtendsDemo1 {
  3. public static void main(String[] args) {
  4. Zi zi = new Zi();
  5. zi.age = 15;
  6. zi.des();
  7. }
  8. }
  9. class Fu{
  10. int age;
  11. public void des(){
  12. System.out.println("父类的des方法"+"---父类的成员变量---"+age);
  13. }
  14. }
  15. class Zi extends Fu{
  16. int age;
  17. public void des(){
  18. super.age = 10;
  19. super.des();
  20. System.out.println("子类的des方法"+"---子类的成员变量---"+age);
  21. }
  22. }


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

  1. package day09.itcast02;
  2. public class ExtendsDemo2 {
  3. public static void main(String[] args) {
  4. Son son = new Son();
  5. }
  6. }
  7. class Father{
  8. Father(){
  9. System.out.println("Father is running");
  10. }
  11. }
  12. class Son extends Father{
  13. Son(){
  14. System.out.println("Son is running");
  15. }
  16. }

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

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

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

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

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

三、final关键字

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

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

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



四、抽象类

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

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

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

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

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

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

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

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

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

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

代码体现:
  1. package day09;
  2. public class AbstractDemo {
  3. public static void main(String[] args) {
  4. Dog d = new Dog();
  5. d.show();
  6. d.speak();
  7. }
  8. }
  9. abstract class Animal{
  10. public void show(){
  11. System.out.println("Animal");
  12. }
  13. public abstract void speak();
  14. }
  15. class Dog extends Animal{
  16. public void speak(){
  17. System.out.println("小狗叫");
  18. }
  19. }


案例:
需求:公司中程序员有姓名,工号,薪水,工作内容。
项目经理除了有姓名,工号,薪水,还有奖金,工作内容。
分析:程序员和项目经历都属于公司里的员工,而且他们都有共性:姓名、工号、薪水以及工作内容
  1. package day09.itcast02;
  2. public class AbstractDemo {
  3. public static void main(String[] args) {
  4. Programer p = new Programer("张三","448",5000);
  5. Manager m = new Manager("李四","500",5000,2500);
  6. p.content();
  7. p.show();
  8. m.content();
  9. m.show();
  10. }
  11. }
  12. abstract class Employee{
  13. String name;
  14. String number;
  15. int salary;
  16. public Employee(String name,String number,int salary){
  17. this.name = name;
  18. this.number = number;
  19. this.salary = salary;
  20. }
  21. public abstract void content();
  22. public abstract void show();
  23. }
  24. class Programer extends Employee{
  25. public Programer(String name,String number,int salary){
  26. super(name,number,salary);
  27. }
  28. public void content(){
  29. System.out.println("敲代码");
  30. }
  31. public void show(){
  32. System.out.println(name+"---"+number+"---"+salary);
  33. }
  34. }
  35. class Manager extends Employee{
  36. int pay;//奖金
  37. public Manager(String name,String number,int salary,int pay){
  38. super(name,number,salary);
  39. this.pay = pay;
  40. }
  41. public void content(){
  42. System.out.println("写规划");
  43. }
  44. public void show(){
  45. System.out.println(name+"---"+number+"---"+salary+"---"+pay);
  46. }
  47. }


五、接口

当一个抽象类中的方法都是抽象方法的时候,这个抽象类就有另外一个表现方式,叫做接口(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、降低了耦合性。

三、多态

什么是多态?
举例子:学生和工人,但是他们都是人中的某一类,这就是多态。多态就是一种事物的不同体现。
多态的体现:
父类的引用或者接口的引用指向了自己的子类对象。
好处:提高了程序的扩展性。
弊端:通过父类的引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法
多态的前提:继承或者实现。
多态通常都有重写操作。
代码体现:
  1. package day10.itcast01;
  2. public class DuotaiDemo1 {
  3. public static void main(String[] args) {
  4. Person p = new Student();
  5. p.eat(p);
  6. Person p1 = new Worker();
  7. p1.eat(p1);
  8. }
  9. }
  10. abstract class Person{
  11. public void eat(Person p){
  12. if(p instanceof Student ){
  13. System.out.println("吃饭");
  14. p.method();
  15. }else if( p instanceof Worker){
  16. System.out.println("吃饭");
  17. p.method();
  18. }
  19. }
  20. public abstract void method();
  21. }
  22. class Student extends Person{
  23. public void method(){
  24. System.out.println("学习");
  25. }
  26. }
  27. class Worker extends Person{
  28. public void method(){
  29. System.out.println("工作");
  30. }
  31. }



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

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

如果想要使用子类的特有方法,只有子类的对象才能使用,这时就需要使用向下转型。
向下转型属于强制类型转换,向上转型是自动类型转换。
  1. package day10.itcast01;
  2. public class DuotaiDemo1 {
  3. public static void main(String[] args) {
  4. Person p = new Student();
  5. p.eat(p);
  6. // p.write(); 报错
  7. ((Student)p).writer();
  8. Person p1 = new Worker();
  9. p1.eat(p1);
  10. ((Worker)p1).work();
  11. }
  12. }
  13. abstract class Person{
  14. public void eat(Person p){
  15. if(p instanceof Student ){
  16. System.out.println("吃饭");
  17. p.method();
  18. }else if( p instanceof Worker){
  19. System.out.println("吃饭");
  20. p.method();
  21. }
  22. }
  23. public abstract void method();
  24. }
  25. class Student extends Person{
  26. public void method(){
  27. System.out.println("学习");
  28. }
  29. public void writer(){
  30. System.out.println("学生的特有方法:写作业");
  31. }
  32. }
  33. class Worker extends Person{
  34. public void method(){
  35. System.out.println("工作");
  36. }
  37. public void work(){
  38. System.out.println("工人的特有方法:干活");
  39. }
  40. }

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

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

4、题目
看一下代码分析打印结果(通过画图分析)
  1. package day10.itcast02;
  2. public class DuoTaiTest3
  3. {
  4. public static void main(String[] args)
  5. {
  6. Fu f = new Zi();
  7. System.out.println("main :" +f.getNum());
  8. }
  9. }
  10. class Fu
  11. {
  12. int x = 4;
  13. Fu()
  14. {
  15. System.out.println("A fu() : "+getNum());
  16. }
  17. int getNum()
  18. {
  19. System.out.println("B fu getnum run...."+x);
  20. return 100;
  21. }
  22. }
  23. class Zi extends Fu
  24. {
  25. int x = 5;
  26. Zi()
  27. { //super();默认存在并调用父类的无参构造
  28. System.out.println("C zi() : "+getNum());
  29. }
  30. int getNum()
  31. {
  32. System.out.println("D zi getnum run...."+x);
  33. return 200;
  34. }
  35. }

结果分析:
分析这道题首先从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类中有两个常用的方法
equlas和toString方法
equals:通常用来比较两象是个对否相等。
toString:返回对象的字符串表现形式,Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于:
getClass().getName() + '@' + Integer.toHexString(hashCode())

一般来说equals和toString方法都需要被重写
案例:
  1. package cn.test;
  2. public class EqualsDemo {
  3. public static void main(String[] args) {
  4. Student s1 = new Student("谢群",18);
  5. Student s2 = new Student("谢群",22);
  6. boolean result = s1.equals(s2);
  7. System.out.println("result:"+result);
  8. System.out.println(s2.toString());
  9. s2.setAge(18);
  10. result = s1.equals(s2);
  11. System.out.println("result:"+result);
  12. System.out.println(s2.toString());
  13. }
  14. }
  15. class Student{
  16. private String name;
  17. private int age;
  18. public Student(String name, int age){
  19. this.name = name;
  20. this.age = age;
  21. }
  22. public Student(){}
  23. public void setName(String name){
  24. this.name = name;
  25. }
  26. public String getName(){
  27. return name;
  28. }
  29. public void setAge(int age){
  30. this.age = age;
  31. }
  32. public int getAge(){
  33. return age;
  34. }
  35. public boolean equals(Object s){
  36. if(s == null){
  37. return false;
  38. }
  39. if(!(s instanceof Student)){
  40. return false;
  41. }
  42. if(s == this){
  43. return true;
  44. }
  45. Student s1 = (Student)s;
  46. return this.name.equals(s1.name) && this.age == s1.age;
  47. }
  48. public String toString(){
  49. return this.name +"----"+this.age;
  50. }
  51. }

















声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/812538
推荐阅读
相关标签
  

闽ICP备14008679号