黑马程序员——面向对象最全总结:谁说程序员没有“对象”
来源:互联网 发布:海云数据 编辑:程序博客网 时间:2024/05/22 15:08
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
使用内部类时的调用情况
static特点:
1,随着类的加载而加载,随着类的消失而消失。说明它的生命周期最长。
2,优先于的对象存在(静态先存在,对象后存在)
3,被所有对象所共享
4,可以直接被类名所调用
实例变量和类变量的区别:
1,存放位置。
类变量:随着类的加载而存在于方法区中
实例变量:随着对象的建立而存在于堆内存中
2,生命周期:
类变量:生命周期随着类的消失而消失
实例变量:生命周期随着对象的消失而消失
静态使用注意事项:
1,静态方法只能访问静态成员(非静态方法既可以访问静态也可以访问非静态)
2,静态方法中不可以定义this,super关键字。(因为静态优先于对象存在。所以静态方法中不可以出现this)
3,主函数是静态的。
静态的利弊
利处:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。
可以直接被类名调用。
弊端:生命周期过长。
访问出现局限性。(静态虽好,只能访问静态。)
注意:
改进版本:将cat类作为参数,传递给一个专门用来调用Cat类中的eat()方法的方法,提高代码复用性
改进版本3:利用多态性,直接运用父类
向下转型:猫提升为Animal后,失去了猫的特有方法,如果此时想要再调用猫的特有方法时,如何操作?
错误代码:把动物转成猫的类型
有instanceof 时的处理方法
前言
本文将以我个人对于该部分内容的理解,重新对整个体系进行归纳和划分,以一种不太常见的方式把面向对象的基础内容全部涵盖在内。我把整个面向对象的知识分成两大部分:第一部分是“功能”,第二部分是“关系”。功能部分包括:类的定义、类的初始化、类的实例化、类的修饰、类的扩展几个部分。关系部分包括:实例与类的关系、子类与父类的关系、类与类的关系几个部分。具体内容将在下文详述。
“异常处理”这部分的内容也常常被一些国内外教科书划分在面向对象的章节中,不过由于我认为异常处理部分内容的体系比较有特点,基本上属于自成体系,也有不少内容,将单独再写一篇文章来进行总结和演练。
功能
类的定义:内部类
基本定义与格式
一句话概述:将一个类定义在另一个类里面。
格式1:通用格式
<pre name="code" class="java">class outerClassName{private class innerClassName{//body of inner class}}
格式2:静态内部类
class outerClassName{private static class innerClassName{//body of inner class}}
格式3:匿名内部类
new ClassOrInterface(){//class-body}
内部类与非内部类的比较
不使用内部类的普通调用/* * notInner想要访问Outer中的field或method, * 需要在notInner类中创建一个Outer类的对象 */class generalClass{//fieldint num = 3;//methodpublic void printStar(){//非静态方法System.out.println("*");}public static void printComma(){//静态方法System.out.println(",");}}class notInner{//field//methodpublic static void main(String[] args) {generalClass.printComma();//静态方法可以直接用类名调用generalClass ot = new generalClass();//非静态方法需要先创建一个实例,在notInner类中创建一个Outer类的对象int getNum = ot.num;//notInner类要访问Outer中的field:num,需要在创建实例后才能进行ot.printStar();//notInner类要访问Outer中的method:printStar(),需要在创建实例后才能进行}
/*内部类的访问规则:1,内部类可以直接访问外部类中的成员,包括私有。之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this2,外部类要访问内部类,必须建立内部类对象。*/class Outer//外部类{private String str = "Outer的私有变量";class Inner//内部类//Class Inner还可以被私有修饰 private class Inner,因为它也属于Outer类的成员{String str = "Inner中的变量";void function(){String str = "function中的变量";System.out.println("innner :"+str);//该语句将会输出:innner :function中的变量System.out.println("innner :"+this.str);//该语句将会输出:innner :Inner中的变量,与Inner.this.str输出结果一致System.out.println("innner :"+Outer.this.str);//Outer后不加this,想要访问Outer中的field会报错,该语句将会输出:innner :Outer的私有变量System.out.println("innner :"+Inner.this.str);//Inner后不加this,想要访问Inner中的field会报错,该语句将会输出:innner :Inner中的变量}}void method()//在innerInOuter中定义的method方法{Inner in = new Inner();//外部类要访问内部类,必须建立内部类对象。in.function();//外部类访问内部类中的function方法}}class innerClass{public static void main(String[] args) {Outer iio = new Outer();iio.method();//直接访问内部类中的成员。Outer.Inner in = new Outer().new Inner();//如果使用Inner in = new Inner();会报错,当内部类被私有时,则不能被访问到in.function();}}
静态内部类和非静态内部类的区别
当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问局限。
class staticInnerClassDemo{//在外部其他类中,直接访问非static内部类的methods比较Outer.Inner in = new Outer().new Inner();in.function();//在外部其他类中,直接访问static内部类的非静态methodsnew Outer.Inner().function();//在外部其他类中,直接访问static内部类的静态methodsOuter.Inner.function();}
内部类必须是static的两种情况
1.当内部类中定义了静态成员
2.外部类中的静态方法需要访问内部类
当描述事物时,事物的内部还有事物,该事物用内部类来描述,因为内部事物在使用外部事物的内容。
内部类可以定义在成员位置上,也可以定义在方法内部。定义在成员位置上时,就可以被私有化,也可以被static所修饰。
如果内部类定义在内部,则不能被静态所修饰,也不能私有化。
定义在方法内部的内部类
内部类定义在局部时,
1,不可以被成员修饰符修饰
2,可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。
1,不可以被成员修饰符修饰
2,可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。
class Outer{int x = 3;void method(final int a){final int y = 4;//内部类定义在局部时,不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。//定义在该method内部的内部类class Inner{void function(){System.out.println(y);}}new Inner().function();//如果不new一个Inner的对象,没有这句语句,将不会运行。没对象,不运行}}class InnerClassDemo{public static void main(String[] args) {Outer out = new Outer();out.method(7);//出栈后释放out.method(8);//又进栈后开辟新的a}}
匿名内部类
匿名内部类:
1,匿名内部类其实就是内部类的简写格式。
2,定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
3,匿名内部类的格式: new 父类或者接口(){定义子类的内容}
4,其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。 可以理解为带内容的对象。
5,匿名内部类中定义的方法最好不要超过3个。
1,匿名内部类其实就是内部类的简写格式。
2,定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
3,匿名内部类的格式: new 父类或者接口(){定义子类的内容}
4,其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。 可以理解为带内容的对象。
5,匿名内部类中定义的方法最好不要超过3个。
class Outer{int x = 3;//没有使用匿名内部类时的情况:以下/*class Inner extends AbsDemo{int num = 90;void show(){System.out.println("show :"+num);}}public void function(){new Inner.show();}*///使用匿名内部类时的情况:以下public void function(){//匿名内部类,直接用AbsDemo创建对象,后面还带着代码块{}new AbsDemo()//创建的是AbsDemo的匿名子对象{void show(){System.out.println("show :"+num);}}.show();//注意:分号在这里,整个代码块是一个整体,是AbsDemo的子类对象,最后是方法调用。匿名对象对方法只能调用一次}}
类的初始化
1.构造函数
对象一建立就会调用与之对应的构造函数。
构造函数的作用:可以用于给对象进行初始化。
构造函数的小细节:
1.当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造函数。
2.当在类中自定义了构造函数后,默认的构造函数就没有了
构造函数的作用:可以用于给对象进行初始化。
构造函数的小细节:
1.当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造函数。
2.当在类中自定义了构造函数后,默认的构造函数就没有了
一个对象建立,构造函数只运行一次。
而一般方法可以被该对象调用多次。
而一般方法可以被该对象调用多次。
什么时候定义构造函数呢?
当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中。
当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中。
/* 程序输出结果: 学号:003姓名:张飞 学号:004姓名:关羽 学号:001姓名:刘备职务:班长 学号:002姓名:诸葛亮职务:学习委员 */public class constructorDem {public static void main(String[] args) {Students stu1 = new Students("003","张飞");//1号张飞,学号如果设置成int,则001将会输出1,所以要用String来存学号Students stu2 = new Students("004","关羽");//2号关羽StuLeader sl1 = new StuLeader("001","刘备","班长");//班长刘备StuLeader sl2 = new StuLeader("002","诸葛亮","学习委员");//学习委员诸葛亮//打印输出,否则控制台没有显示结果System.out.println(stu1);System.out.println(stu2);System.out.println(sl1);System.out.println(sl2);}}class Students{private String number;//学号private String name;//姓名//构造函数public Students(String number, String name){this.setNumber(number);this.setName(name);}//学号的getter和setterpublic String getNumber() {return number;}public void setNumber(String number) {this.number = number;}//姓名的getter和setterpublic String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString(){return "学号:" + number + "\t" + "姓名:" + name ;}}class StuLeader extends Students{//学生干部是一个特殊的学生的类,继承自学生,多一个职务的属性//Implicit super constructor Students() is undefined for default constructor. Must define an explicit constructor//因为在父类Students中没有定义默认的构造器,所以必须要在该类中定义一个显式的构造器。因为在子类隐式的构造器开头隐含一个super();private String duty;public StuLeader(String number, String name, String duty){super(number,name);//不能写成super(int number,String name);this.duty = duty;}@Overridepublic String toString(){return super.toString() + "\t" + "职务:" + duty;//如果学号不对齐,使用tab键并调用super的时候会发生一些问题}}
2.构造代码块
作用:给对象进行初始化
对象一建立就运行,并优先于构造函数运行
和构造函数的区别:
构造代码块是给所有对象进行统一初始化,
而构造函数是给对应的对象初始化。
构造代码块是给所有对象进行统一初始化,
而构造函数是给对应的对象初始化。
构造代码快中定义的是不同对象共性的初始化内容。
/*运用构造代码块,小孩不管有名字没名字,生下来都会哭了小孩生下来哭了小孩:Bob小孩生下来哭了 */class Baby{private String name;{System.out.println("小孩生下来哭了");}Baby(){}Baby(String name){this.name =name;}@Override //重写的toString不能放在构造函数中间,还需要在主函数中打印输出对象public String toString(){return "小孩:" + name + "\n";}}class ConDem2{public static void main(String[] args) {//有名字的小孩Baby b = new Baby("Bob");System.out.println(b);//没名字的小孩new Baby();}}
类的实例化
匿名对象
匿名对象使用方式一:当对对象的方法只调用一次时,可以用匿名对象来完成
匿名对象使用方式二:可以将匿名对象作为实际参数进行传递。
匿名对象调用field没意义
class Car{String name = "奥迪";String color = "红色的";void run(){System.out.println(color + name + "在欢乐地奔跑");} }class AnonymousInstance {public static void main(String[] args) {//正常创建对象的方法Car redAudi = new Car();redAudi.run();//匿名对象使用方式一:当对对象的方法只调用一次时,可以用匿名对象来完成new Car().name = "被黑客改了名字的奥迪";new Car().run();//即使改了名字,这句话输出的结果还是:红色的奥迪在欢乐地奔跑//因为new Car().run();在创建之前,new Car().name = "被黑客改了名字的小土车";在内存中就已经被销毁了,匿名对象调用field没意义changeCar(redAudi);//程序将会输出:黑色的奥迪在欢乐地奔跑}//匿名对象使用方式二:可以将匿名对象作为实际参数进行传递。public static void changeCar(Car c){c.color = "黑色的";c.run();}}
Person p = new Person("张飞",20);这句话都做了什么
1,因为new用到了Person.class.所以会先找到Person.class文件并加载到内存中。
2,执行该类中的static代码块,如果有的话,给Person.class类进行初始化。
3,在堆内存中开辟空间,分配内存地址。
4,在堆内存中建立对象的特有属性。并进行默认初始化。
5,对属性进行显示初始化。
6,对对象进行构造代码块初始化。
7,对对象进行对应的构造函数初始化。
8,将内存地址付给栈内存中的p变量。
2,执行该类中的static代码块,如果有的话,给Person.class类进行初始化。
3,在堆内存中开辟空间,分配内存地址。
4,在堆内存中建立对象的特有属性。并进行默认初始化。
5,对属性进行显示初始化。
6,对对象进行构造代码块初始化。
7,对对象进行对应的构造函数初始化。
8,将内存地址付给栈内存中的p变量。
单例设计模式
单例设计模式的作用:解决一个类在内存只存在一个对象。
保证对象唯一的目的:
1,为避免其他程序过多建立该类对象,先禁止其他程序建立该类对象
2,又必须u让其他程序可以访问到该类对象,只好在本类中,自定义一个对象
3,为方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
1,为避免其他程序过多建立该类对象,先禁止其他程序建立该类对象
2,又必须u让其他程序可以访问到该类对象,只好在本类中,自定义一个对象
3,为方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
创建单例设计模式的步骤:
1,将构造函数私有化
2,在类中创建一个本类对象
3,提供一个可以获取到该对象的方法
2,在类中创建一个本类对象
3,提供一个可以获取到该对象的方法
1.饿汉式(Eager)
这个是先初始化对象,称为:饿汉式。
class Singleton{//1,将构造函数私有化。private Singleton(){}//2,在类中创建一个本类对象private static Singleton s = new Singleton();//静态方法只能访问静态变量,故此处只能是static。类和对象都可以属于field//3,提供一个可以获取到该对象的方法public static Singleton getInstance()//需要通过该方法才能访问对象,但是方法被调用只能通过对象、类名两种方式//现在没有对象,故需要用类名调用,必须是static方法{return s;}}class SingleDemo {Singleton sgt = Singleton.getInstance();//不能再通过new来创建对象了,只能通过调用Singleton中的getInstance方法}
2.懒汉式(Lazy)
对象是方法被调用时,才初始化,也叫做对象的延时加载。称为:懒汉式。
Single类进内存,对象还没有存在,只有当调用了getInstance方法时,才建立对象。
class Singleton2{//1,将构造函数私有化。private Singleton2(){}//2,在类中创建一个本类对象private static Singleton2 s = null;//3,提供一个可以获取到该对象的方法public static Singleton2 getInstance(){if(s==null){synchronized(Singleton2.class)//多线程部分的内容{if(s==null)s = new Singleton2();}}return s;}}
类的修饰符
1.Static
Static一言以蔽之:是属于类自带的
用法:是一个修饰符,用于修饰成员(成员变量,成员函数).
当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,
还可以直接被类名调用。类名.静态成员。
用法:是一个修饰符,用于修饰成员(成员变量,成员函数).
当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,
还可以直接被类名调用。类名.静态成员。
static特点:
1,随着类的加载而加载,随着类的消失而消失。说明它的生命周期最长。
2,优先于的对象存在(静态先存在,对象后存在)
3,被所有对象所共享
4,可以直接被类名所调用
实例变量和类变量的区别:
1,存放位置。
类变量:随着类的加载而存在于方法区中
实例变量:随着对象的建立而存在于堆内存中
2,生命周期:
类变量:生命周期随着类的消失而消失
实例变量:生命周期随着对象的消失而消失
静态使用注意事项:
1,静态方法只能访问静态成员(非静态方法既可以访问静态也可以访问非静态)
2,静态方法中不可以定义this,super关键字。(因为静态优先于对象存在。所以静态方法中不可以出现this)
3,主函数是静态的。
静态的利弊
利处:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。
可以直接被类名调用。
弊端:生命周期过长。
访问出现局限性。(静态虽好,只能访问静态。)
class Bike{public static String color = "red";public static void run(){System.out.println("自行车跑啊跑");}}public class StaticDemo {public static void main(String[] args) {//普通调用方式调用Bike的field中的colorBike giant = new Bike();System.out.println(giant.color);//类变量的另一种调用方式System.out.println(Bike.color);//效果与第一种调用方式相同//调用类变量的方法Bike.run();}}
2.Static Initializer:静态代码块
静态代码块。
格式:
static
{
静态代码块中的执行语句。
}
特点:随着类的加载而执行,只执行一次,并优先于主函数。
用于给类进行初始化的。
格式:
static
{
静态代码块中的执行语句。
}
特点:随着类的加载而执行,只执行一次,并优先于主函数。
用于给类进行初始化的。
即使构造代码块定义在静态代码块之前,还是先执行静态代码块的内容
class StaticCode{int num = 9;//构造函数StaticCode(){System.out.println("构造函数的内容被打印了");}//构造代码块{System.out.println("构造代码块的内容被打印了 " + this.num);}//静态代码块static{System.out.println("静态代码块的内容被打印了");}//构造函数超载StaticCode(int x){System.out.println("构造函数超载的内容被打印了");}//静态方法public static void show(){System.out.println("show run");}}class StaitcInitializer {static{//System.out.println("b");}public static void main(String[] args) {new StaticCode(4);//因为没有创建过StaticCode的对象,"构造函数的内容被打印了"这行不会被打印}}/* 程序输出结果 静态代码块的内容被打印了:即使构造代码块定义在静态代码块之前,还是先执行静态代码块的内容构造代码块的内容被打印了 9构造函数超载的内容被打印了*/
类的扩展
1.抽象类
抽象类的特点:
1,抽象方法一定在抽象类中。
2,抽象方法和抽象类都必须被abstract关键字修饰。
3,抽象类不可以用new创建对象。因为调用抽象方法没意义。
4,抽象类中的抽象方法要被使用,必须由子类复写起所有的抽象方法后(非抽象方法可以不用实现),建立子类对象调用。
1,抽象方法一定在抽象类中。
2,抽象方法和抽象类都必须被abstract关键字修饰。
3,抽象类不可以用new创建对象。因为调用抽象方法没意义。
4,抽象类中的抽象方法要被使用,必须由子类复写起所有的抽象方法后(非抽象方法可以不用实现),建立子类对象调用。
5,如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
abstract 关键字旁边,和哪些关键字不能共存?
final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。
private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。而抽象方法出现的就是需要被复写。
static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法运行没意义。
final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。
private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。而抽象方法出现的就是需要被复写。
static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法运行没意义。
模版方法:
在定义功能时,功能的一部分是确定的,但是有一部分是不确定,而确定的部分在使用不确定的部分,
那么这时就将不确定的部分暴露出去。由该类的子类去完成。
在定义功能时,功能的一部分是确定的,但是有一部分是不确定,而确定的部分在使用不确定的部分,
那么这时就将不确定的部分暴露出去。由该类的子类去完成。
abstract class GetTime//把GetTime的功能定义成抽象类,模板设计模式{public final void getTime()//之所以要使用final关键字,是为了不让getTime被复写{long start = System.currentTimeMillis();runcode();long end = System.currentTimeMillis();System.out.println("毫秒:"+(end-start));}public abstract void runcode();//要求子类复写该抽象方法,以得到不同代码相对应的运行时间}class SubTime extends GetTime{public void runcode()//开始复写父类方法{for(int x=0; x<4000; x++){System.out.print(x);}}}class AbstractDemo{public static void main(String[] args) {SubTime gt = new SubTime();gt.getTime();}
2.接口
接口定义时,格式特点:
1,接口中常见定义:常量,抽象方法。
2,接口中的成员都有固定修饰符。
常量:public static final
方法:public abstract
1,接口中常见定义:常量,抽象方法。
2,接口中的成员都有固定修饰符。
常量:public static final
方法:public abstract
注意:
1,接口中的成员都是public的。
2,接口是不可以创建对象的,因为有抽象方法。
3,接口需要被子类实现,子类对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类。
4,接口可以被类多实现,也是对多继承不支持的转换形式。java支持多实现。
2,接口是不可以创建对象的,因为有抽象方法。
3,接口需要被子类实现,子类对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类。
4,接口可以被类多实现,也是对多继承不支持的转换形式。java支持多实现。
abstract class Student{abstract void study();void sleep(){System.out.println("sleep");}}interface Smoking//抽烟不是学生都具有的行为,定义为接口{void smoke();}class ZhangSan extends Student implements Smoking{void study(){}public void smoke(){}}
关系
子父类关系特点
1.子父类中变量的特点
如果子类中出现非私有的同名成员变量时,
子类要访问本类中的变量,用this。this:代表的是本类对象的引用。
子类要访问父类中的同名变量,用super。super:代表的是父类对象的引用。
子类要访问本类中的变量,用this。this:代表的是本类对象的引用。
子类要访问父类中的同名变量,用super。super:代表的是父类对象的引用。
class Fu {public String str = "爹的成员";}class Zi extends Fu{String str = "儿的方法";//一般不会这么定义,因为子类可以从父类中获取void show(){System.out.println(str);//儿的成员System.out.println(this.str);//儿的成员System.out.println(super.str);//爹的成员}}class ExtendsDemo {public static void main(String[] args) {Zi z = new Zi();z.show();}}
2.子父类中方法的特点
当子类出现和父类一模一样的函数时,子类对象调用该函数,会运行子类函数的内容。如同父类的函数被覆盖一样。
这种情况是函数的另一个特性:Override(覆盖)
当子类继承父类,沿袭了父类的功能,到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,
这时,没有必要定义新功能,而是使用覆盖特性,保留父类的功能定义,并重写功能内容。
这种情况是函数的另一个特性:Override(覆盖)
当子类继承父类,沿袭了父类的功能,到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,
这时,没有必要定义新功能,而是使用覆盖特性,保留父类的功能定义,并重写功能内容。
覆盖:
1,子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
2,静态只能覆盖静态。
1,子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。
2,静态只能覆盖静态。
class Father{void methods(){System.out.println("爹的方法");}}class Son extends Father{void methods(){System.out.println("儿的方法");}}class ExtendsDemo2{public static void main(String[] args) {Son z = new Son();z.methods();//儿的方法}}
3.子父类中构造函数的特点
在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句 super();
super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();
为什么子类一定要访问父类中的构造函数?
super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();
为什么子类一定要访问父类中的构造函数?
因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。所以子类在对象初始化时,要先访问一下父类中的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
多态(Polymorphism)
1.多态的概念
多态:可以理解为事物存在的多种体现形态。
多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。
多态的前提
必须是类与类之间有关系。要么继承,要么实现。
通常还有一个前提:存在覆盖。
多态的利弊
利:多态的出现大大的提高程序的扩展性。
弊:只能使用父类的引用访问父类中的成员。
多态的应用
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。
多态的前提
必须是类与类之间有关系。要么继承,要么实现。
通常还有一个前提:存在覆盖。
多态的利弊
利:多态的出现大大的提高程序的扩展性。
弊:只能使用父类的引用访问父类中的成员。
多态的应用
2.多态引入的目的
最原始版本:为了让Cat类中的不同实例调用eat方法,需要反复创建该类的实例
<pre name="code" class="java">abstract class Animal{abstract void eat();}class Cat extends Animal{public void eat(){System.out.println("吃鱼");}public void catchMouse(){System.out.println("抓老鼠");}}class Dog extends Animal{public void eat(){System.out.println("吃骨头");}public void watchHome(){System.out.println("看家");}}public class PolyDemo {public static void main(String[] args) {Cat heimao = new Cat();heimao.eat();Cat lanmao = new Cat();lanmao.eat();}}
public class PolyDemo {public static void main(String[] args) {Cat heimao = new Cat();//heimao.eat();getEatmethod(heimao);Cat lanmao = new Cat();//lanmao.eat();getEatmethod(lanmao);}public static void getEatmethod(Cat c){//将cat类作为参数,传递给一个专门用来调用Cat类中的eat()方法的方法c.eat();}}
改进版本2:将Dog类作为参数,传递给一个专门用来调用Dog类中的eat()方法的方法,运用方法的重载
public class PolyDemo {public static void main(String[] args) {Cat heimao = new Cat();getEatmethod(heimao);Dog xiaohuang = new Dog();getEatmethod(xiaohuang);//或者直接getEatmethod(new Dog());}public static void getEatmethod(Cat c){//将cat类作为参数,传递给一个专门用来调用Cat类中的eat()方法的方法c.eat();}public static void getEatmethod(Dog d){//将Dog类作为参数,传递给一个专门用来调用Dog类中的eat()方法的方法,运用方法的重载d.eat();}}
改进版本3:利用多态性,直接运用父类
public class PolyDemo {public static void main(String[] args) {getEatmethod(new Cat());getEatmethod(new Dog());}public static void getEatmethod(Animal a){//直接传入父类即可//Animal a = new Cat();//Animal a = new Dog();a.eat();}
3.多态的转型
向上转型:类型提升,引用数据类型的提升
Animal a = new Cat();//类型提升。 向上转型。猫的类型提升为Animal
向下转型:猫提升为Animal后,失去了猫的特有方法,如果此时想要再调用猫的特有方法时,如何操作?
Cat c = (Cat)a;//强制将父类的引用a。转成子类类型Cat。向下转型。此时就可以使用c.catchMouse()了
错误代码:把动物转成猫的类型
<pre name="code" class="java">Animal a = new Animal();//千万不要出现这样的操作,就是将父类对象转成子类类型。//我们能转换的是父类引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换。//多态自始至终都是子类对象在做着变化。Cat c = (Cat)a;
4. instanceof
假如没有instanceof,出现类型转换异常时的情况:
public class PolyDemo2 {public static void main(String[] args) {method(new Cat());method(new Dog());//传入method后,等于是把狗强制转换成了猫,会报类型转换异常}public static void method(Animal a){a.eat();Cat c = (Cat)a;//向下转型,转型后就可以抓老鼠了c.catchMouse();}}/*吃鱼Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat抓老鼠吃骨头at PolyDemo2.method(PolyDemo2.java:40)at PolyDemo2.main(PolyDemo2.java:35)*/
public static void method(Animal a)//Animal a = new Cat();{a.eat();/*小细节:不要把父类写在上面if(a instanceof Animal){System.out.println("haha");}else */if(a instanceof Cat){Cat c = (Cat)a;c.catchMouse();}else if(a instanceof Dog){Dog c = (Dog)a;c.kanJia();}/*instanceof : 用于判断对象的类型。 对象 intanceof 类型(类类型 接口类型) */}
5.多态中成员的特点
在多态中成员函数的特点:在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。在运行时期:参阅对象所属的类中是否有调用的方法。简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。在多态中,成员变量的特点:无论编译和运行,都参考左边(引用型变量所属的类)。在多态中,静态成员函数的特点:无论编译和运行,都参考做左边。
Object类:所有类的父类
Object:是所有对象的直接后者间接父类。该类中定义的肯定是所有对象都具备的功能。Object类中已经提供了对对象是否相同的比较方法。如果自定义类中也有比较相同的功能,没有必要重新定义。只要沿袭父类中的功能,建立自己特有比较内容即可。这就是覆盖。
class Demo{ //extends Object}class Person{}class ObjectDemo {public static void main(String[] args) {Demo d1 = new Demo();Demo d2 = new Demo();Demo d3 = d1;System.out.println(d1.equals(d2));//输出:falseSystem.out.println(d3.equals(d1));//输出:trueSystem.out.println(d1.toString());//输出:Demo@659e0bfdSystem.out.println(d2.toString());//输出:Demo@2a139a55System.out.println(d1);//输出语句打印对象时,会自动调用对象的toString方法。打印对象的字符串表现形式。输出:Demo@659e0bfdClass c = d1.getClass();System.out.println(c.getName());//输出:DemoSystem.out.println(c.getName()+"@"+Integer.toHexString(d1.hashCode()));//输出:Demo@659e0bfd//比较Person对象Person p1 = new Person();Person p2 = new Person();System.out.println("Person: " + p1.equals(p2));//输出:Person: false}}
自己定义一个比较的方法,版本1:
class Demo{ //extends Objectprivate int num;Demo(int num){this.num = num;}public boolean compare(Demo d){return this.num == d.num;}}class ObjectDemo {public static void main(String[] args) {Demo d1 = new Demo(4);Demo d2 = new Demo(3);Demo d3 = new Demo(4);System.out.println(d1.compare(d2));//输出:falseSystem.out.println(d1.compare(d3));//输出:true}}
自己定义一个比较的方法,版本2:
父类Object中已经提供了比较的方法,不用再自己写compare方法
可以自行复写Object中的equals方法
class Demo{ //extends Objectprivate int num;Demo(int num){this.num = num;}public boolean equals(Object obj){//Object obj = new Demo();Demo d = (Demo)obj; return this.num == d.num;//直接访问obj.num将会报错,找不到Object类中的num,想使用子类的东西,需要向上转型,然后访问d.num}}class ObjectDemo {public static void main(String[] args) {Demo d1 = new Demo(4);Demo d2 = new Demo(3);Demo d3 = new Demo(4);System.out.println(d1.equals(d2));//输出:falseSystem.out.println(d1.equals(d3));//输出:true}}
0 0
- 黑马程序员——面向对象最全总结:谁说程序员没有“对象”
- 黑马程序员——面向对象总结
- 黑马程序员——面向对象总结
- 黑马程序员——面向对象总结
- 黑马程序员—面向对象知识总结
- 黑马程序员—面向对象
- 黑马程序员—面向对象
- 黑马程序员—面向对象
- 黑马程序员 面向对象总结
- 黑马程序员-面向对象总结
- 黑马程序员-面向对象总结
- 【黑马程序员】面向对象总结
- 黑马程序员 Java面向对象——集合框架总结
- 黑马程序员-Java基础总结04——面向对象
- 黑马程序员--Java面向对象——集合框架总结
- 黑马程序员——java基础面向对象基础知识总结
- 黑马程序员---------Java面向对象——异常总结
- 黑马程序员——面向对象入门总结
- TortoiseSVN解决冲突
- 对Netty的11个疑问
- SSL构建单双向https认证!https部署及注意事项!
- IP实时传输协议RTP/RTCP
- C#泛型类之List<T>
- 黑马程序员——面向对象最全总结:谁说程序员没有“对象”
- 前台jsp获取后台查询的结果集数据并实现分页显示
- Effective C++——条款48(第7章)
- 项目打包签名时候报:Export aborted because fatal lint errors were found these are listed in the lint view...
- 【Android Studio】为Android Studio设置HTTP代理
- android studio shortcut
- OKHttp源码解析
- 总结面试题
- mysql分区分表