java进阶--继承类

【1】构造函数

为了在遵守某些约定的情况下对已有的程序进行扩充,java语言和一般的op语言一样拥有继承。继承是为了扩展,继承不是为了修改。

这里我们谈几点java继承机制中容易忽略但是很重要的几点。

1.子类中的构造函数

假如我们的超类中显示声明了一个构造函数,子类的实例化能用默认的构造函数么?答案是不能! 比如下面这个例子,子类中必须显示声明。

public class third {
public static void main(String atgs[])
{
   a a1=new a(1, "i am class a");
   System.out.print(a1.id+" "+a1.name);
}
}

class a extends b{
	a(int i,String n){//超类没有不带参数的构造函数 所以必须显示声明
		super(i, n);
	}
}
class b{
	public int id;
	public String name;
	b(int i,String n){
		id=i;
		name=n;
	}
}

我们得弄清楚构造函数不是方法也不可以被继承,所以依赖与超类的子类必须传递参数给超类。

2.构造函数的形式

上面我们讲了构造函数不是方法,那么他是什么?其实没有确切的准确的形容词来概括他,实际上类的初始化工作可以有不同的形式。

形式一 构造函数

形式二 定义的时候初始化(这在c++里面是不允许的,但是java可以)

形式三 构造块 这个比较特殊,用{}在类中来表示这是构造块,它可以执行普通语句 就和在函数里写一样 ,但是不需要定义方法名。

class b{
	public int id=0;//定义初始化
	public String name;

	{//初始化块
		System.out.println(id);
		System.out.println(name);
	}
	b(){//无参数构造函数

	}
	b(int i,String n){//带参数构造函数
		id=i;
		name=n;
	}
}

3.构造函数的顺序依赖

这个是老话题了,递归调用,从object类开始。

【2】继承和再定义成员

1.重载和覆盖方法

有的时候我们需要保留超类的某个函数并且对它的适用范围进行扩展,这时候我们就用到重载,重载的方法很简单:声明和超类同名但是参数不同的方法即可。

覆盖适用于不想保留超类方法的时候,我们可以在子类中用一个同名同参数的方法去覆盖超类中的方法。

覆盖的时候我们遵循子类不可以修改超类的约定,对于方法的访问权限只能是越来越宽松,比如可以从private修改到public,因为这被视作是子类对超类的扩展。但是反过来就不行,因为这样修改了超类的协议,超类中原本可以访问的方法子类却把它屏蔽了。因为如果我们使用超类构造一个对象但是我们实际引用了子类的实例,那么原来父类中private的方法会暴露为public,但是这不影响正确性,反过来就不行。

例如下面的代码是不会编译通过的

class a extends b{
	a(int i,String n){//超类没有不带参数的构造函数 所以必须显示声明
		super(i, n);
	}
	private void b_method(){
		System.out.print("a overload b_method!");
	}
}

class b{
         public  void b_method() {

	}

	b(int i,String n){//带参数构造函数
	id=i;
	name=n;
  }
}

2.隐藏域

域不会被覆盖,但是它可以被隐藏。如果在子类中声明和超类一样的域超类中的域依然会存在,但是不能直接通过域名去访问他。必须通过super或者是超类的引用去访问他。

如下:

public class third {
public static void main(String atgs[])
{
   b a1=new a(1, "a");//超类引用子类
   a a2=new a(2, "a");//子类
   System.out.println(a1.classname);
   System.out.println(a2.classname);
}
}

class a extends b{
	public String classname="class a";
	a(int i,String n){//超类没有不带参数的构造函数 所以必须显示声明
		super(i, n);
	}
	public void b_method(){
		System.out.print("a overload b_method!");
	}
}

class b{
	public String classname="class b";
	public int id=0;//定义初始化
	public String name;
	public  void b_method() {

	}
	{//初始化块
		System.out.println(id);
		System.out.println(name);
	}
	b(){//无参数构造函数

	}
	b(int i,String n){//带参数构造函数
		id=i;
		name=n;
	}
}

但是方法的访问是不一样的,总的来说引用类型决定域,真实类型决定方法。这样就造成了不统一的问题,假如说我用一个超类类型引用子类对象,那么这个引用的域是超类的域,方法是子类的方法!!如下所示:

public static void main(String atgs[])
{
   b a1=new a(1, "a");//超类引用子类
   System.out.println(a1.classname);
   a1.b_method();
}
}

//输出结果:

class b

a overload b_method!

正因为这个原因,我们鼓励通过存取器(get/set函数)来操纵超类的域,因为一个子类当中的方法访问和超类中相同名字的域的时候他会选择本类中的域而不会去访问超类中的同名域,也就是说如果我们通过 a1.get_classnam()来获取域的话得到的就是class a。这样方法和域都是具体类的方法和域!!

【3】类型兼容和转换

1.兼容性

在类型层次中,层次越高兼容性越广。因为类型层次越高越不具体,覆盖范围越广。类型层次越低越具体,细节越丰富,覆盖范围越窄。

比如说:动物是超类 猫,狗是他的子类

动物兼容猫和狗,所以我们可以 猫 猫1=new 动物(),狗 狗2=new 动物()·····

反过来就不行,因为你不能说动物是狗。当一个高次的类型引用低层次的对象的时候 不会发生问题,这种转换我们称做宽转换。相反就会发生问题,称作窄转换。

2.类型测试

通过使用 instanceof 运算符就可以检查对象的类,如果其左边的表达式与右边的类型名是赋值兼容的话那么就会返回 true 否则返回 false 。

instanceof用来做一具体的类型判断,在网上看到一个例子还是挺好的。

instanceof有一些用处。比如我们写了一个处理账单的系统,其中有这样三个类:

public class Bill {//省略细节}
  public class PhoneBill extends Bill {//省略细节}
  public class GasBill extends Bill {//省略细节}

  在处理程序里有一个方法,接受一个Bill类型的对象,计算金额。假设两种账单计算方法不同,而传入的Bill对象可能是两种中的任何一种,所以要用instanceof来判断:

  public double calculate(Bill bill) {
  if (bill instanceof PhoneBill) {
  //计算电话账单
  }
  if (bill instanceof GasBill) {
  //计算燃气账单
  }
  ...
  }
  这样就可以用一个方法处理两种子类。

  然而,这种做法通常被认为是没有好好利用面向对象中的多态性。其实上面的功能要求用方法重载完全可以实现,这是面向对象变成应有的做法,避免回到结构化编程模式。只要提供两个名字和返回值都相同,接受参数类型不同的方法就可以了:

  public double calculate(PhoneBill bill) {
  //计算电话账单
  }

  public double calculate(GasBill bill) {
  //计算燃气账单
  }

所以,使用instanceof在绝大多数情况下并不是推荐的做法,应当好好利用多态。

3.protected的确切含义

protected设计被用于一些“超类只对子类和同一个包的代码开放访问”的场合。

这里有几点要注意的

1.不同包,类自身的实例能访问protected成员么?不能!!假如我在package a里面有一个类class_a 我在另一个package里面导入package a并且实例化了一个class_a对象,这个对象是无法访问protected成员的。听起来有些匪夷所思,但是的确是这样的。

2.不同包,子类能访问超类的protected成员么?可以!!!

3.不同子类能通过对方访问相同父类的protected成员么? 不可以!!!

总的来说,只有子类内部或者是同一个包可以访问超类的protected成员,这与设计protected这个类型的初衷是一致的!!!

【4】Object类

object类是所有类的基类,位于最顶层。object类里面有所有类都具有的方法,他们分别是equals,hashcode,clone,getclass,finalize,tostring。用法不一一详细讲,这里面比较特别的是equals,clone,hashcode。

1.equals方法区别两个对象是否在内容上相同,注意是内容!!!‘==’用于判断两个对象是否指向同一个引用。

2.clone 方法返回一个与原对象指向同一类型的对象,但是需要注意的是这个对象只是原对象的浅拷贝,要想深拷贝原对象需要重载clone方法。(注意clone方法本身是protected的!!)

3.hashcode需要注意是因为他和equals的关系,这个需要大家回想一下hash存储算法。hash存储算法中,我们每次存储数据都会根据数据生成一个hashcode,这个code下面可能已经存有数据了,如果现有的数据和要存储的数据相同(注意:这里的相同判断就是用 equals !!!)那么我们就放弃存储。

【5】单继承和多继承

关于单继承和多继承 java的答案是只有单继承。为什么不支持多继承,主要原因是为了规避一些多继承带来的风险。这里有个很好的例子,假如超类有方法fun,超类有两个子类a,b他们重载了方法fun。这时候有个类c继承了a和b,那么c里面的fun方法该来自谁呢?就好像避免精神分裂带来的危险一样,子类的特点必须和唯一的超类一致。

为了解决摈弃多继承的缺点,所以java里面引进了接口。

时间: 2024-10-25 11:00:56

java进阶--继承类的相关文章

java进阶--嵌套类和接口

public class third { private int id1;//私有成员 public int id2; public static class class_top{ void sayid(third th){//静态嵌套类想要访问顶层类的私有/公有成员只能通过传递引用对象 th.id1=12; th.id2=12; } } public static class static_class extends class_top{ static void say_id(third th

【代码笔记】Java基础:类的继承(构造器)

在Java中,创建对象的格式为: 类名 对象名 = new 类名(): 如: 1 JFrame jf = new JFrame(); 一个对象被创建出来时,经常要先做一些事这个对象才能正常使用,也可以说是准备工作,比如给这个对象赋些初始值,或者先运行一些别的方法.这时候就需要一个“构造器”用于: 构造对象 初始化属性这样,把那些在创建时要做的事写在构造方法里,每次创建就会被执行一遍. 我们常见的一种Java格式: public 返回类型 方法名(参数){ } 如: 1 public void s

JAVA进阶之旅(二)——认识Class类,反射的概念,Constructor,Fiald,Method,反射Main方法,数组的反射和实践

JAVA进阶之旅(二)--认识Class类,反射的概念,Constructor,Fiald,Method,反射Main方法,数组的反射和实践 我们继续聊JAVA,这次比较有意思,那就是反射了 一.认识Class类 想要反射,你就必须要了解一个类--Class,我们知道,java程序中的各个java类都属于同一事物,我们通常用Classliability描述对吧,反射这个概念从JDK1.2就出来了,历史算是比较悠久了,这个Class可不是关键字哦,这个是一个类,他代表的是一类事物: 我们归根结底就

Java中继承,类的高级概念的知识点

1. 继承含义 在面向对象编程中,可以通过扩展一个已有的类,并继承该类的属性和行为,来创建一个新的类,这种方式称为继承(inheritance). 2. 继承的优点 A.代码的可重用性 B.子类可以扩展父类的属性和方法 C.父类的属性和方法可用于子类 D.设计应用程序变得更加简单 3. 如何判断类具有继承关系? 类与类的关系:  a. has—a(组合关系) b. is—a(继承关系) 4. Java中继承的实现(关键字:extends)   关键字extends出现在类声明时的类名后,exte

java 继承类与接口问题

java 先extends 继承类,再implements 继承接口 1 public class DataBase extends ClassBase implements Ijiekou { 2 3 }// end

Java进阶01 String类

之前的java基础系列中讨论了Java最核心的概念,特别是面向对象的基础.在Java进阶中,我将对Java基础进行补充,并转向应用层面. 大部分编程语言都能够处理字符串(String).字符串是有序的字符集合,比如"Hello World!".在Java中,字符串被存储为String类对象.调用字符串对象的方法,可以实现字符串相关的操作. String类包含在java.lang包中.这个包会在Java启动的时候自动import,所以可以当做一个内置类(built-in class).我

Java 线程--继承java.lang.Thread类实现线程

现实生活中的很多事情是同时进行的,Java中为了模拟这种状态,引入了线程机制.先来看线程的基本概念. 线程是指进程中的一个执行场景,也就是执行流程,进程和线程的区别: 1.每个进程是一个应用程序,都有独立的内存空间. 2.同一个进程中的线程共享其进程中的内存和资源. (共享的内存是堆内存和方法区内存,栈内存不共享,每个线程有自己的栈内存) 我们还需要了解以下基本内容: 1.什么是进程? 一个进程对应一个应用程序.例如:在Windows操作系统启动word就表示启动了一个进程.在Java开发环境下

java进阶之反射:反射基础之如何获取一个类以及如何获取这个类的所有属性和方法(1)

java学习一段时间之后,大家可能经常会听到反射这个词,那么说明java已经学习到一个高一点的层次了.接下来我会一步步和大家一起揭开java高级特性反射的神秘面纱. 首先介绍下类对象这个概念,可能会经常用到这个概念: 类对象:java中有句很经典的话"万事万物皆对象",相信大家都不陌生,这句话告诉了我们java的特征之一,那就是面向对象.java中类的概念我们都很熟悉,既然万事万物皆是对象,那么类是谁的对象呢?<对象的概念:一个类的实例>换句话说,类是谁的实例.如此就有了类

java进阶06 线程初探

线程,程序和进程是经常容易混淆的概念. 程序:就是有序严谨的指令集 进程:是一个程序及其数据在处理机上顺序执行时所发生的活动 线程:程序中不同的执行路径,就是程序中多种处理或者方法. 线程有两种方法实现 一:继承Thread 覆盖run方法 package Thread; public class Thread1 { public static void main(String[] args){ MyThread1 thread1=new MyThread1(); thread1.setName