前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java继承特性以及重写现象的内存分析

Java继承特性以及重写现象的内存分析

作者头像
何处锦绣不灰堆
发布2020-05-29 09:40:20
5920
发布2020-05-29 09:40:20
举报
文章被收录于专栏:农历七月廿一农历七月廿一

今天我们说一下Java面向对象中的一个特性-继承,然后做一下他的内存分析,理解一下重新现象的情况。

怎么理解继承?

下面先介绍一下怎么理解继承的特性,继承呢在Java中的关键是extends,那么其实所谓的继承是比较简单的也是很好理解的,Java中如果一个类继承了父类,那么我们就说他们是一个继承的关系,那么被继承的那个类的所有属性,继承者都是存在的,除了构造器,构造器是不可以被继承的用final修饰的方法不可以被继承,举个例子:

代码语言:javascript
复制
package com.gaojizu.TestExtends;
/**
 * 测试继承   继承谁的类,那么他具有的所有属性继承者都有   除了构造器  构造器是不可以继承的  java
 * 的类只有单继承,接口是有多继承的,如果没有定义继承,我们默认的都是继承Object他是我们的祖类
 * 是在java.lang.Object
 * @author admin
 *
 */
//动物
public class Animals {
	String eyes;
	public void run() {
		System.out.println("我可以跑");
	}
	public void eat() {
		System.out.println("我可以吃");
	}
    public final void visit() {
		System.out.println("我可以旅行");
	}
	
}
//哺乳动物
	class Mammel extends Animals{
 		public void taisheng() {
			System.out.println("我是胎生");
		}
}

写一个测试类:

代码语言:javascript
复制
package com.gaojizu.TestExtends;
/**
 * 测试继承
 * @author admin
 *
 */
public class Test {

	public static void main(String[] args) {
		Mammel m = new Mammel();
		m.eat();
		
	}
}

输出结果:我可以吃!

那么我们可以看到,我写的Mammel 也就是哺乳动物的类中是没有eat()方法的,eat方法是在他的父类中的,所以说他是拥有了父亲的方法,这是很简单的,但凡了解Java的人基本都是明白的,前面说了,构造器是不可以被继承的,我们可以测试一下!

代码语言:javascript
复制
public class Animals {
	String eyes;
	public void run() {
		System.out.println("我可以跑");
	}
	public void eat() {
		System.out.println("我可以吃");
	}
	/**
    * 创建一个构造器
    */
	public Animals() {
		
	}
	
	public Animals(String name) {
		
	}
	
}

那么在测试的Mammel类里面我们是不可以调用到这个构造方法的。

讲了很多废话,我们今天主要是做内存分析的, 不过呢考虑到有些人对继承现象比较晕,所以简单的做一个介绍。

什么是重写?

所谓的重写就是说,我们拿到父类的方法以后,满足不了我们的需求,需要自己定义内容的时候,我们可以将父类的方法重新定义,从而呢实现一个覆盖的现象!举个例子:

代码语言:javascript
复制
public class Animals {
	String eyes;
	public void run() {
		System.out.println("我可以跑");
	}
	public void eat() {
		System.out.println("我可以吃");
	}
	
	public Animals() {
		
	}
	
	public Animals(String name) {
		
	}
	
}
//哺乳动物
	class Mammel extends Animals{
        //重写父类的eat方法
		public void eat() {
			System.out.println("我可以烧火吃饭");
		}
 		public void taisheng() {
			System.out.println("我是胎生");
		}
}

测试一下:

代码语言:javascript
复制
Mammel m = new Mammel();
		m.eat();
		//输出:我可以烧火吃饭

那么之前的eat就被覆盖了,毕竟是人嘛,总不能吃不煮的食物吧,高级动物嘛!

这里有人就说了,我两个都想用怎么办呢?就是我不仅仅要改变父类的实现内容,我还要使用父类自己的实现内容,是不是可以呢?可以的, 我们每一个方法都是有两个隐式参数的,一个是this一个是super,我们可以测试一下:

代码语言:javascript
复制
//哺乳动物
	class Mammel extends Animals{
		public void eat() {
			super.eat();
			System.out.println("我可以烧火吃饭");
		}
 		public void taisheng() {
			System.out.println("我是胎生");
		}
}

测试一下:

代码语言:javascript
复制
	Mammel m = new Mammel();
		m.eat();
		//输出:   我可以吃  我可以烧火吃饭  

ok到这里概念就基本介绍完了,下面我们根据代码看一下内存的情况:

哦,这里有一点是忘记说了的,就是我们不管写不写继承,就是说不管我们一个类是不是写了extends他都是默认继承基类的,什么是基类的,就是类的祖先-Object,怎么确定呢?前面我们说了,既然继承了,就一定是有他父类的方法的,对不对,那么我们不写关键字,看看是不是可以使用Object的方法就行了,我们先看一下他有哪些方法:

举个例子:

写个不做继承的类:

代码语言:javascript
复制
	//测试基类的使用
	class TestObject{
		
	}

测试一下:

代码语言:javascript
复制
TestObject t = new TestObject();
		t.toString();
		System.out.println(t.toString());

我们可以看到,我们是可以使用toString方法的,说明我们是可以直接继承基类的方法的。

下面我们画张图进行简单的分析一下:

画的不好,将就看一下,不要太辣眼睛...

我们可以看到,一个类被创建出来,实例化一个对象以后,我们在使用的时候他会先找到父类,父类会继续找他的父类,为什么呢?可以看到我每一个类的下面都写了一个 super(),为什么呢?我之前是不是说了,每一个方法都是有一个隐式参数的,this和super,this指向的是本类,super指向的就是父类,那么这里代码会首先走super(),这个super必须放在代码的第一行,否则是错的,即使你不写,JVM也会帮你自动创建一个super(),代码走到super继续向上找父类,直到找到Object基类结束,那么内存里面的分布情况就是右边画的,最下面的是Paxing类,那么他就有上面所有的方法和属性,我们可以一级一级的想嘛,他有父类的所有方法和属性,除构造器以外的,那么就是有Anmals的所有属性和方法,那么Anmils又继承了Object类,他就有Object的所有属性和方法,自然Paxing就有所有的属性和方法了,所以是包含的关系。

就是说如果是一个类没有写任何的继承,他就是基类的直接儿子,也就是他是直接从属于Object类的,这里再说一下super关键字,他指向的是直接父类的方法,不是祖父的方法。也就是说隶属于直接上级!

但是他是不是可以重写祖父的类的方法呢?当然是可以的,举个例子:

代码语言:javascript
复制
public class Animals {
	
	String eyes;
	public void run() {
		System.out.println("我可以跑");
	}
	public void eat() {
		System.out.println("我可以吃");
	}
	public void hun() {
		System.out.println("woshi 孙子的测试");
	}
	
	public Animals() {
		
	}
	
	public Animals(String name) {
		
	}
	
}
//哺乳动物
	class Mammel extends Animals{
		
		public void eat() {
			super.eat();
			System.out.println("我可以烧火吃饭");
		}
 		public void taisheng() {
			System.out.println("我是胎生");
		}
}
	//继续继承
	class Testsunzi extends Mammel{
        //测试重写祖类的方法
		public void hun() {
			System.out.println("我是新的测试孙子的函数");
		}
		public void eat() {
			System.out.println("我是孙子");
		}
	}

测试一下可以看出来:

代码语言:javascript
复制
	Testsunzi tt = new Testsunzi();
		tt.eat();
		tt.hun();

输出的是:

是和我们想的一样的,所以是没有问题的。

到这里基本就是结束了,其实继承的特性是很厉害的,他的作用很多,但是主要的是为了提高代码的复用性,这个不用说了,大家都是知道的。

分析内存的一个好处是可以帮助我们更好的理解代码的执行情况,对于我们理解代码也是由帮助的,其实对于调试代码记忆解决常见的错误也是很有帮助的。

补充一点:

是不是没有继承就不可以实现代码复用了呢?当然不是,我们使用组合也是一样的,那么其实组合要比继承来的个更加的灵活了和方便,我们可以看个例子:

代码语言:javascript
复制
package com.gaojizu.TestZuhe;
/**
 * 测试组合的使用
 * @author admin
 *
 */
public class TestZuheClass {
	
}
class Anmils{
	public void eat() {
		System.out.println("我可以吃");
	}
}
class Dog{
	Anmils a = new Anmils();
	public void eat() {
		a.eat();
		System.out.println("我也可以吃");
	}
}

测试一下:

代码语言:javascript
复制
package com.gaojizu.TestZuhe;
/**
 * 测试组合类的使用
 * @author admin
 *
 */
public class TestDog {

	public static void main(String[] args) {
		Dog d = new Dog();
		d.eat();
	}
}

输出结果:

这里其实可以看到,他是比继承要灵活方便一些的,那么到底使用哪一个呢?遵循一个原则:

IS-A关系的使用继承:什么意思呢?就是谁是谁的谁的时候,使用继承,狗是动物,是吧,使用继承!我举的例子不是的,不要按照我的来,我不按照套路出牌的!哈哈

HAS-A关系的使用组合:谁拥有谁,谁包含谁的使用组合,电脑包含主板,是吧,使用组合就行了,就是自己感觉是用哪一个合适就是用哪一个就行了!这个只是便于理解罢了!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-10-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档