java多态

Posted 茂树24

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java多态相关的知识,希望对你有一定的参考价值。

实例一、

class Parent{
	String s = "s  parent";
	public Parent() {
		System.out.println("Parent 构造器");
		System.out.println(this);
		this.fun(12);
		this.f();
		this.p();
	}
	void p(){
		System.out.println("P");
	}
	public static void f(){
		System.out.println("Parent's static f()");
	}
	
	private void fun(int i){
		System.out.println("Parent's fun()");
	}
}

class Child extends Parent{
	String s = "s  Child";
	public Child(){
		System.out.println("Child 构造器");
		System.out.println(this);
		this.fun(12);
		this.f();
	}
	
	public static void f(){
		System.out.println("Child's static f()");
	}
	
	public void fun(int i){
		System.out.println("Child's fun()");
	}
}

public class Demo{

	public static void main(String[] args) {
		System.out.println("Parent t = new Child()------->");
		Parent t = new Child();
		System.out.println("/////////"+t.s);
		System.out.println("Parent p = new Parent()------>");
		Parent p = new Parent();
		System.out.println("Child c = new Child()--------> ");
		Child c = new Child();
	    System.out.println("///////////"+c.s);
	}
	
}

/*OutPut:

Parent t = new Child()------->

Parent 构造器

[email protected]

Parent‘s fun()

Parent‘s static f()

P

Child 构造器

[email protected]

Child‘s fun()

Child‘s static f()

/////////s  parent

Parent p = new Parent()------>

Parent 构造器

[email protected]

Parent‘s fun()

Parent‘s static f()

P

Child c = new Child()-------->

Parent 构造器

[email protected]

Parent‘s fun()

Parent‘s static f()

P

Child 构造器

[email protected]

Child‘s fun()

Child‘s static f()

///////////s  Child

 */





技术分享

技术分享

* invokespecial 前期绑定,调用超类构造方法 实例初始化方法,私有方法  

* invokevirtual  后期绑定  调用实例方法   */

 

当程序开始编译成.class文件时,发生了许多事情:

在编译Parent的时候,开始编译构造器,此时将这个构造方法被编译成invokespecial类型。

接着开始编译构造器里面的语句:

  当编译System.out.println(this)时候,此时的this是不确定的所以是invokevirtual类型。

接着执行this.fun(12);对于这个this被解释成invokespecial还是invokevirtual?

  由于这个fun()函数在Parent中是static的,所以是静态方法,只能在编译期间进行绑定,所以是invokespecial 。

  对于this.f()方法,因为这个方法是一个普通的方法,所以可能存在后期绑定的行为,所以this是一个invokevirtual 类型。

  同样的对于this.a()方法,虽然从代码上看是Parent特有的方法,但是编译器并不会知道,所以也会将这个this作为invokevirtual  对待。

  然后就是编译剩下的方法。同样在Child中原理一样。

 

  然后在main方法中对于域的调用,不存在是否有后期绑定的行为,并且编译器仅仅就只是能够识别引用的类型,所以t.s为s  Parent。在类的构造器中也是如此,加入在Parent构造器中加入this.s  那么编译器就假定认为这个this就是“invokespecial”就是指当下的这个类。


实例二:



class SuperParent {
	SuperParent(){
		System.out.println("SuperParent:---> ");
		this.f();
	}
	 void f(){
		System.out.println("SuperParent");
	}
}

class Parent extends SuperParent{
	Parent(){
		System.out.println("Parent:--->");
		this.f();
		super.f();
	}
	void f(){
		System.out.println("Parent");
	}
}

class Child extends Parent{
	public Child() {
		System.out.println("Child:--->");
		this.f();
		super.f();
	}
	void f(){
		System.out.println("Child");
	}
}

public class Demo{

	public static void main(String[] args) {
		Child c = new Child();
	}

	
}

/*Output:

SuperParent:--->

Child

Parent:--->

Child

SuperParent

Child:--->

Child

Parent

*/


技术分享

技术分享


 编译器编译SuperParent时,遇到this.f()调用,此时f函数是一个一般的方法,所以y这个this应该是invokevirtual,所以应该是在后期绑定的时候由jvm判断到底调用那个f方法。

  接着编译Parent方法,同理处理构造器中this.f( )。但是对于super来说,这个对于编译器是确定的就是其基类,不会待运行的时候有什么后期绑定,所以应该是invokespecial的。同理分析Child。

 

 

  总之,编译器是不会有判断一个引用指向对象的类型的,它仅仅只是知道引用当下的类型,

同时对于this,与super到底指代的是谁,关键是要看调用额对象,如果对象是一个一般的方法,可能这个方法就是被重载后的,否则一般都是在编译的时候就会绑定。

所以来说:

多态优点:消除了类型之间的耦合关系,增加了代码的可扩展性。

多态缺点:对基类依赖性太高,更加偏向于前期绑定,对动态绑定机制享有的太少仅仅是子类中对父类重写代码部分。并且要用多态前提就是必须有继承,还要有覆盖操作。对设计类的时候有所限制。

 

目前多态情况下不能访问子类特有的成员。解决这个缺点问题的方法就是:强制类型转化

Eg:

类型转换最场景的问题: java.lang.ClassCastException。 强制类型转换失败。

 

实例三:



class print{
	print(){
		System.out.println("print调用了");
	}
}

class SuperParent{
	print t = new print();
	public SuperParent() {
		System.out.println("我是SuperParent的构造器");
	}
	{
		System.out.println("我是SuperParent的构造代码块");
	}
	
	private static int x = printInit("我是SuperParent的静态变量");
	static {
		System.out.println("我是SuperParent的static代码块");
	}
	
	
	
	public static int printInit(String s){
		System.out.println(s);
		return 0;
	}
	
}

class Parent extends SuperParent{
	public Parent() {
		System.out.println("我是Parent的构造器");
	}
	
	private  int x = printInit("我是Parent的成员变量");
	
	{
		System.out.println("我是Parent的构造代码块");
	}
	static {
		System.out.println("我是Parent的static代码块");
	}
	
	print t = new print();
}

class Child extends Parent{
	public Child() {
		System.out.println("我是Child的构造器");
	}
	{
		System.out.println("我是Child的构造代码块");
	}
	
	static {
		System.out.println("我是Child的static代码块");
	}
	
	private  int x = printInit("我是Child的 成员 变量");
}

public class Demo{
	public static void main(String[] args) {
		Child c = new Child();
	}
}


/*OutPut:

我是SuperParent的静态变量

我是SuperParent的static代码块

我是Parent的static代码块

我是Child的static代码块

print调用了

我是SuperParent的构造代码块

我是SuperParent的构造器

我是Parent的成员变量

我是Parent的构造代码块

print调用了

我是Parent的构造器

我是Child的构造代码块

我是Child的 成员 变量

我是Child的构造器

 

*/

 

 技术分享

技术分享


对与一个类,在编译的时候,编译器会按照类中构造代码块和成员对象(即使是引用对象)的顺序加载到构造器内语句的前面,其中如果内有初始化的会执行默认初始化,然后将static变量和static代码块按照顺序 加载到一个static{}代码块中.

 

   特别是执行默认初始化这一步是非常重要的,可以避免一些不必要的错误.比如:如果构造器只是在构造对象过程中的一个步骤,并且该对象所属的类是从这个构造器所属的类中导出的,那么导出部分在当前构造器正在调用的时刻中是没有初始化的。然后,一个动态绑定的方法调用却会向外深入到继承层次结构的内部,它可以调用导出类中的方法。但是可想而知,这个方法中所操纵的成员时还没有初始化的,有可能出错。并且很难排查。

 

 对于多个类,当main函数中得知待用Child类,就会开始加在child但是又发现有基类接着加载基类以此类推,直到加载最后一个基类为止,此时从根基类开始现将所有的static成员按照从基类到导出类的顺序执行完。这样做的好处就是防止在导出类中使用基类中的static成员。接着按照调用基类的顺序开始执行非静态的内容比如构造器等。

实例四、多态的应用:

1. 多态用于形参类型的时候,可以接收更多类型的数据 。

  避免了因基类导出的多个子类在调用各自的某一方法时要多写代码,更能体现出继承的关系,同时利用向上转型可以使得多个子类在调用方法功能相同行为不同的方法出现冗余,同时再添加新类,也变得灵活。提高的可扩展性。

 

2. 多态用于返回值类型的时候,可以返回更多类型的数据。

//图形类
abstract class MyShape{

	public abstract void getArea();

	public abstract void getLength();	
}



class Circle extends MyShape{

	public static final double PI = 3.14;

	double r;

	public Circle(double r){
		this.r =r ;	
	}

	public  void getArea(){
		System.out.println("圆形的面积:"+ PI*r*r);
	}

	public  void getLength(){
		System.out.println("圆形的周长:"+ 2*PI*r);
	}
}


class Rect  extends MyShape{

	int width;

	int height;

	public Rect(int width , int height){
		this.width = width;
		this.height = height;
	}

	public  void getArea(){
		System.out.println("矩形的面积:"+ width*height);
	}

	public  void getLength(){
		System.out.println("矩形的周长:"+ 2*(width+height));
	}
}



class Demo12 {

	public static void main(String[] args) 
	{
		/*
		//System.out.println("Hello World!");
		Circle c = new Circle(4.0);
		print(c);

		Rect r = new Rect(3,4);
		print(r);
		*/

		MyShape m = getShape(0); //调用了使用多态的方法,定义的变量类型要与返回值类型一致。
		m.getArea();
		m.getLength();
		

	}

	//需求1: 定义一个函数可以接收任意类型的图形对象,并且打印图形面积与周长。
	public static void print(MyShape s){ // MyShpe s = new Circle(4.0);
		s.getArea();
		s.getLength();
	}
	// 需求2: 定义一个函数可以返回任意类型的图形对象。
	public static MyShape  getShape(int i){
		if (i==0){
			return new Circle(4.0);
		}else{
			return new Rect(3,4);
		}
	}


}


















以上是关于java多态的主要内容,如果未能解决你的问题,请参考以下文章

每个人单核苷酸多态性(SNP)形成的原因是啥?

多态性和动态铸造

Java多态——代码复用性

Java 多态

java多态/重载方法——一个疑难代码引发的讨论

Java 多态