一些概念
1、封装
定义:
隐藏对象的属性和实现细节,仅对外提供公共访问方式
好处:
a、将变化隔离
b、便于使用
c、提高重用性
d、提高安全性
原则:
把不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共方法对其访问
个人理解:
你不需要知道我是什么人也不需要知道我会干什么,你只要明白我能给你你想要的,别的什么都不要问不要管。
2、修饰符
private
私有修饰符,权限修饰符,用于修饰类中的成员(成员变量,成员方法)只在本类中有效
私有仅仅是封装的一种表现形式
个人理解:
我就是一个保镖,保护你的隐私不被窥探。
static
修饰符,用于修饰成员–多个对象拥有共同数据
特点
a、随着类的加载而加载–静态会随着类的消失而消失
b、优先于对象存在
c、被所有对象共享
d、可以直接被类名调用
使用注意事项
1.静态方法只能访问静态成员,非静态方法可以访问静态成员
2.静态方法中不可以定义this,super等关键字,因为静态优先于对象存在,所以不可能出现this
3.主函数是静态的
什么时候使用静态
1、静态变量:对象中出现共享数据时;
特有数据要定义成非静态存在于堆内存中
2、静态函数:功能内部没有访问到非静态数据(对象的特有数据)那么可以定义为静态
静态利弊
利:对对象的共享数据进行单独空间存储,节省空间;可以直接被类名调用
弊:生命周期长,访问有局限性
Hello(不知道写啥)
当成员被static修饰时,就多了一个调用方式,除了可以被对象调用外,还可以被类名调用,类名.静态成员
实例变量与类变量区别
1.存放位置–类变量随着类的加载而存在于方法区中,实例变量是随着对象的建立而存在于堆内存中
2.生命周期:类变量是生命周期最长的,随着类的消失而消失;实例变量生命周期随着对象的消失而消失
final修饰符:可以修饰类,函数,变量
被final修饰的类不可以被继承,避免被继承,被子类复写
被final修饰的方法不可以被复写
被final修饰的变量是常量,只能赋值一次,既可以修饰成员变量又可以修饰局部变量
内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量
3、构造函数、构造代码块和静态代码块
构造函数:
对象一建立就会调用与之对应的构造函数
作用:用于给对象进行初始化
构造代码块:
给对象进行初始化,对象已建立就运行,而且优于构造函数执行
静态代码块
随着类的加载而执行,只执行一次,并优先于主函数,用于给类初始化
构造函数和构造代码块的区别
构造代码块优先于构造函数;构造代码块是给所有对象共性进行初始化,构造函数是对特定对象进行初始化
子父类的构造方法
三个类:祖父类,父类,子类,层次继承,都有一个属性,都有无参和有参两种构造方法
使用子类无参构造方法创建子类对象时:
使用子类有参构造方法创建子类对象时:
可以看出来,不管子类用什么构造方法创建对象都会所继承的父类的无参构造方法。默认的当没有定义构造方法时,每个类都是有一个无参构造方法,但是当定义了有参构造方法则没有了无参构造方法,而每当子类创建对象时都需要用到父类的无参构造方法,所以如果父类定义了有参构造方法时,必须手动的在定义一个无参构造方法,不然编译的时候就会报错。
4、继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
好处
1.提高代码的复用性
2,让类之间产生关系,有了这个关系才有了多态
注意
使用一个体系中功能时:查阅父类功能,创建子类对象使用功能
不要为了获取其他类功能简化代码而继承,必须是类与类之间有所属关系才可以继承
重载与重写(覆盖)
重载
指同一个类中的多个方法具有相同的名字,但这些方法具有不同的参数列表,即参数的数量或参数类型不能完全相同
注意: 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
重写(覆盖)
(1) 父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。
但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。
方法重写又称方法覆盖。
(2)若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。
如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
(3)子类函数的访问修饰权限不能少于父类的;
注意:
子类中不能重写父类中的final方法
子类中必须重写父类中的abstract方法
这也叫做多态性,重写方法只能存在于具有继承关系中,重写方法只能重写父类非私有的方法。
当上例中Father类speak()方法被private时,Son类不能重写出Father类speak()方法,此时Son类speak()方法相当与在Son类中定义的一个speak()方法。
Father类speak()方法一但被final时,无论该方法被public,protected及默认所修饰时,Son类根本不能重写Father类speak()方法,
这个博客讲的很细,抄不过来了,有兴趣可以看看
5、多态
什么是多态
接口的多种不同的实现方式即为多态。
多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术。
我们在程序中定义的引用变量所指向的具体类型和通过该引用变量的方法调用在编程的时候并不确定,当处于运行期间才确定。就是这个引用变量究竟指向哪一个实例对象,在编译期间是不确定的,只有运行期才能确定,这样不用修改源码就可以把变量绑定到不同的类实例上,让程序拥有了多个运行状态,这就是多态。
多态的实现机制
参见: Java的静态分派与动态分派
多态的分类
a. 编译时多态:方法的重载;
b. 运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态。(我们平时说得多的事运行时多态,所以多态主要也是指运行时多态)
实现多态的技术
动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
多态的作用
消除类型之间的耦合关系。
多态存在的三个必要条件
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。
多态的好处
1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3 所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
举例理解一
a. 运行时多态是在父类引用指向子类对象时产生的。一个父类的引用可以指向多种子类对象,那么运行时对于同一个消息应该如何做出响应呢?这就由实际的被引用的对象的类型来决定。
b. 为什么要有重写呢?难道父类中的方法没有被重写,直接调用子类中存在的方法难道是不行吗?看个例子如下:
public class Solution {
public class Father {
public String name ="father";
// public String getName() {
// return name;
// }
}
public class Son {
public String name ="son";
public String getName() {
return name;
}
}
public static void main(String[] args) {
Solution solution = new Solution();
Solution.Father father =solution.new Father();
System.out.println(father.getName());
}
}
上面的例子中,当父类中的getName()被注释掉以后,调用father.getName()方法会出错。因此,当父类引用指向子类方法时,必须调用那些父类中存在的方法,如果子类中对该方法进行了重写,那么在运行时就会动态调用子类中的方法,这就是多态。
c. 要有继承很好理解,没有继承的话,哪来的重写呢。
举例理解二
public class Solution {
public class A {
public void show(A obj){
System.out.println("A and A");
}
public void show(C obj){
System.out.println("A and C");
}
}
public class B extends A {
public void show(B obj){
System.out.println("B and B");
}
public void show(A obj){
System.out.println("B and A");
}
}
public class C extends B {
}
public static void main(String[] args) {
Solution solution =new Solution();
Solution.A a1=solution.new A();
Solution.A a2=solution.new B();
Solution.B b =solution.new B();
Solution.C c =solution.new C();
a1.show(b);
a1.show(c);
a2.show(b);
a2.show(c);
}
}
对于前两条语句的结果我们很容易理解,那第三条和第四条的,为什么结果和我们想的不一样,不应该是"B and B"吗?要理解这是为什么,我们要先理解下面这句话:
当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。 (但是如果强制把超类转换成子类的话,就可以调用子类中新添加而超类没有的方法了)
看一下标红的那句话,我们知道问题所在了吗?
当运行 a2.show(b) 的时候,实际是要调用一个 show(B obj) 的方法,但是 A 中有这样一个方法吗?没有!但是由于 B 继承自 A,所以会调用 A 中的 show(A obj) 的方法,但是调用时候发现这个方法已经被 B 重写了,所以就会转向来调用 B 中的 show(A obj) 方法。所以才会打印出"B and A"。
实际上这里涉及方法调用的优先问题 ,优先级由高到低依次为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。让我们来看看它是怎么工作的。
比如,a2.show(b),a2是一个引用变量,类型为A,则this为a2,b是B的一个实例,于是它到类A里面找show(B obj)方法,没有找到,于是到A的super(超类)找,而A没有超类,因此转到第三优先级this.show((super)O),this仍然是a2,这里O为B,(super)O即(super)B即A,因此它到类A里面找show(A obj)的方法,类A有这个方法,但是由于a2引用的是类B的一个对象,B覆盖了A的show(A obj)方法,因此最终锁定到类B的show(A obj),输出为"B and A”。
怎么样?理解了吗?
问题还要继续,现在我们再来看上面的分析过程是怎么体现出红色字体那句话的内涵的。它说:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。还是拿a2.show(b)来说吧。
a2是一个引用变量,类型为A,它引用的是B的一个对象,因此这句话的意思是由B来决定调用的是哪个方法。因此应该调用B的show(B obj)从而输出"B and B”才对。但是为什么跟前面的分析得到的结果不相符呢?!问题在于我们不要忽略了蓝色字体的后半部分,那里特别指明:这个被调用的方法必须是在超类中定义过的,也就是被子类覆盖的方法。B里面的show(B obj)在超类A中有定义吗?没有!那就更谈不上被覆盖了。实际上这句话隐藏了一条信息:它仍然是按照方法调用的优先级来确定的。它在类A中找到了show(A obj),如果子类B没有覆盖show(A obj)方法,那么它就调用A的show(A obj)(由于B继承A,虽然没有覆盖这个方法,但从超类A那里继承了这个方法,从某种意义上说,还是由B确定调用的方法,只是方法是在A中实现而已);现在子类B覆盖了show(A obj),因此它最终锁定到B的show(A obj)。这就是那句话的意义所在,到这里,我们可以清晰的理解Java的多态性了。
举例理解三
class A{
public void show(){
show2();
}
public void show2(){
System.out.println("wo");
}
}
class B extends A{
public void show2(){
System.out.println("ai");
}
}
class C extends B{
public void show2(){
System.out.println("ni");
}
}
public class Solution {
public static void main(String[] args) {
A a=new B();
a.show();
B b = new C();
b.show();
}
}
上面例子中的输出是什么呢?答案是:ai ni
有了前一个例子我们就会很容易理解这个例子。在B类中是没有对A中的show方法进行重写,所以当a.show()时调用的是父类中的show方法,父类中show方法调用了show2方法,但是在调用的时候发现show2方法已经被子类重写,因此会调用子类中show2方法,因此输出"ai"。可见,当父类引用指向子类对象的时候,对父类中方法的调用都会绑定到子类中重写后的方法上,如果子类没有对方法进行重写,那么会直接调用父类中的方法,相当于是直接调用从父类继承的方法。
还需要注意的一点是:子类在重写父类的方法时,方法的访问权限不能更低!
FROM:Java方法多态性理解