Java笔记 —— 继承

h2{
color: #4ABCDE;
}
a{
text-decoration: none!important;
}
a:hover{
color: red !important;
text-decoration: underline !important;
}
pre{
border: solid 1px #CCCCCC;
border-radius: 3px;
background-color: #F8F8F8;
margin: 15px;
overflow: auto;
white-space: pre;
font-size: 13px;
font-family: consolas, courier, monospace;
line-height: 20px;
padding: 6px 10px;
tab-size: 4;
}
p.textRight{
text-align: right;
}
p.header{
color: #787878;
font-size: 20px;
font-family: 楷体, "微软雅黑", arial;
font-weight: bold;
}

一、作用

1)复用类

二、super 关键字

1)第一种用法:super 关键字是父类对象的引用


package com.example;

public class Person{ // 父类
public String name="张三";
public int age = 20; public void speak(){
System.out.println("Person: speak()");
}
public void cry(){
System.out.println("Person: cry()");
}
}

package com.example;

public class Man extends Person{ // 子类
public void speak(){
System.out.println("Man: speak()"); super.speak(); // 调用父类的 speak
cry(); // 调用父类的 cry System.out.println("name: " + name); // 调用父类的 name
System.out.println("age: " + super.age); // 调用父类的 age
}
}

package com.example;

public class Test{
public static void main(String[] args){
Man m = new Man();
m.speak();
}
}

运行结果:

Man: speak()
Person: speak()
Person: cry()
name: 张三
age: 20
  • 父类和子类中都有 speak 方法,所以子类中想要调用父类的 speak 方法时,必须要用 super 关键字(这种情况是子类对父类的方法进行了重写)
  • 其他情况可以使用 super 关键字也可以不使用,例如:例子中的 cry(), name, super.age
  • 其实在创建子类对象的时候,父类对象会先被创建,而且父类对象被包裹在子类对象中(可以想象成在 Man 对象中有 Person super = new Person() 这样的代码行,这里的 super 就是父类对象的引用)

2)第二种用法:super() 或者 super(参数列表) 是父类的构造器

例1:


package com.example;

public class Person{ // 父类
public Person(){
System.out.println("Person()");
}
}

package com.example;

public class Man extends Person{ // 子类
public Man(){
super(); // 父类构造器
System.out.println("Man()");
// super(); //error
}
}

package com.example;

public class Test{
public static void main(String[] args){
new Man();
}
}

运行结果:

Person()
Man()
  • 父类构造器只能在子类构造器中调用,不能在子类的方法中调用
  • 父类构造器只能位于子类构造器的第一行
  • 每个子类构造器只能调用一个父类构造器

例2:


package com.example;

public class Person{ // 父类
public Person(int i){
System.out.println("Person" + "(" + i + ")");
}
public Person(String str){
System.out.println("Person" + "(" + str + ")");
}
}

package com.example;

public class Man extends Person{ // 子类
public Man(){
super(1); // 调用父类构造器
System.out.println("Man()");
}
public Man(int i){
super("张三"); // 调用父类构造器
System.out.println("Man" + "(" + i + ")");
}
public Man(String str){
super(2); // 调用父类构造器
System.out.println("Man" + "(" + str + ")");
}
}

package com.example;

public class Test{
public static void main(String[] args){
new Man();
}
}

运行结果:

Person(1)
Man()
  • 如果我们没有给类创建构造器,编译器会为该类自动创建一个默认构造器(无参构造器),该操作在编译时完成,我们看不见。如果我们给类创建了构造器,那么编译器便不会再为类创建默认构造器
  • 当父类中含有默认构造器时,编译器会在子类的构造器中自动调用父类的默认构造器,以完成父类的初始化,该操作也是在编译时完成的,我们看不见
  • 当父类中含有无参构造器时,编译器也会在子类构造器中自动调用父类的无参构造器
  • 当父类中只含有有参构造器时,编译器不会在子类构造器中自动调用父类的构造器,此时我们必须在子类的所有构造器中都对父类中的一个构造器进行调用,否则编译时会报错,例如:例子中子类 Man 中的每一个构造器都调用了父类的构造器
  • 总结:在继承关系中,父类最好不创建构造器或者在创建多个构造器时要创建一个无参构造器,这样才不用每个子类的构造器都对父类的构造器进行调用

三、访问控制权限

1)Java 的访问控制权限有 public,protected,<default>,private 其中如果没有涉及继承关系时,protected 和 <default> 都只能在包内访问


package net.example;

public class S{
public int i = 10;
protected double d = 51.2;
String str1 = "张三"; // <default>
private String str2 = "李四";
}

package com.example;

public class T{
public int i = 10;
protected double d = 51.2;
String str1 = "张三"; // <default>
private String str2 = "李四";
}

package com.example;
import net.example.S; // 导入 S 类 public class Test{
public static void main(String[] args){
S s1 = new S();
System.out.println("S: " + s1.i);
//System.out.println("S: " + s1.d); // error
//System.out.println("S: " + s1.str1); // error
//System.out.println("S: " + s1.str2); // error T t1 = new T();
System.out.println("T: " + t1.i);
System.out.println("T: " + t1.d);
System.out.println("T: " + t1.str1);
//System.out.println("T: " + t1.str2); // error
}
}

运行结果:

S: 10
T: 10
T: 51.2
T: 张三
  • private 可以跨包访问
  • 没有继承关系时,protected 和 <default> 都只能在包内访问。例子中 Test 和 T 在同一个包,Test 和 S 在不同的包,所以 s1.d 和 s1.str1 都不能通过编译,而 t1.d 和 t1.str1 可以通过编译
  • private 只能在同一个类中被访问

2)子类可以访问父类中用 protected 限制的属性和方法,即使子类和父类不在同一个包内


package net.example;

public class Animals{ // 父类
protected int age = 2;
String name = "旺财"; // <default> protected void bark(){
System.out.println("汪汪");
}
}

package com.example;
import net.example.Animals; // 导入 Animals 类 public class Dogs extends Animals{ // 子类
public Dogs(){
System.out.println(age);
//System.out.println(name); // error
bark();
}
}

package com.example;

public class Test{
public static void main(String[] args){
new Dogs();
}
}

运行结果:

2
汪汪
  • 父类中的 name 属性的访问控制权限为 <default> ,所以子类不能调用该属性
  • protected 访问控制权限可以保证只有继承者才能对父类的属性和方法进行访问,当然这是在跨包的情况下,如果某一个类和父类在同一个包,就算这个类不继承父类,那么也可以随意访问父类中由 protected 限制的属性和方法

3)子类要重写父类中的方法时,子类中重写的方法的访问控制权限必须要高于父类的方法的访问控制权限

package com.example;

public class Person{ // 父类
public void p1(){
System.out.println("Person: p1");
}
protected void p2(){
System.out.println("Person: p2");
}
void p3(){
System.out.println("Person: p3");
}
private void p4(){
System.out.println("Person: p4");
}
}

package com.example;

public class Man extends Person{ // 子类
public void p1(){
System.out.println("Man: p1");
} /* public void p2(){
System.out.println("Man: p2");
} */
protected void p2(){
System.out.println("Man: p2");
}
/* void p2(){ // error, protected 的访问控制权限比 <default> 的高
System.out.println("Man: p2");
} */ /* public void p3(){
System.out.println("Man: p3");
}
protected void p3(){
System.out.println("Man: p3");
} */
void p3(){
System.out.println("Man: p3");
} /* public void p4(){
System.out.println("Man: p4");
}
protected void p4(){
System.out.println("Man: p4");
}
void p4(){
System.out.println("Man: p4");
} */
private void p4(){
System.out.println("Man: p4");
}
}

package com.example;

public class Test{
public static void main(String[] args){
Man m = new Man();
m.p1();
m.p2();
m.p3();
//m.p4(); // error
}
}

运行结果:

Man: p1
Man: p2
Man: p3
  • 子类 Man 中的 p2 方法不能用 <default> 访问控制权限进行重写,因为 protected 的控制权限比较高
  • Test 类中不能调用 m.p4() 因为 p4() 的访问控制权限是 private,只能在类的内部进行调用
  • 子类重写父类的方法时,只要重写方法的访问控制权限比父类中的高就行,比如:父类中的 p2 方法由 protected 限制,所以子类 Man 中重写父类的 p2 方法时,可以是public 或者是 protected,但不能是 <default> 和 private

四、初始化顺序

1)初始化顺序:父类静态属性 -> 子类静态属性 -> 父类非静态属性 -> 父类构造器调用 -> 子类非静态属性 -> 子类构造器调用


package com.example;

public class T{
public T(int i){
System.out.println("T" + "(" + i + ")");
}
}

package com.example;

public class Person{ // 父类
public static T t1 = new T(1); // 静态属性
public T t2 = new T(2); // 非静态属性 public Person(){
System.out.println("Person()");
} public static T t3 = new T(3); // 静态属性
}

package com.example;

public class Man extends Person{ // 子类
public static T t4 = new T(4); // 静态属性
public T t5 = new T(5); // 非静态属性 public Man(){
System.out.println("Man()");
} public static T t6 = new T(6); // 静态属性
}

package com.example;

public class Test{
public static void main(String[] args){
new Man();
}
}

运行结果:

T(1)
T(3)
T(4)
T(6)
T(2)
Person()
T(5)
Man()

五、@Override 与方法重写

1)Java 为了保证方法重写时不会出现误写成方法重载的情况,所以引入了 @Override


package com.example;

public class Person{ // 父类
public void speak(int i){
System.out.println("Person: speak" + "(" + i + ")");
}
}

package com.example;

public class Man extends Person{ // 子类

	/* @Override
public void speak(String str){
System.out.println("Man: speak" + "(" + str + ")");
} */ // error,用了 @Override ,那么 @Override 下面的方法必须是方法重写,否则编译时会出错 @Override
public void speak(int i){
System.out.println("Man: speak" + "(" + i + ")");
}
}

package com.example;

public class Test{
public static void main(String[] args){
Man m = new Man();
m.speak(10);
}
}

运行结果:

Man: speak(10)

六、继承抽象类

1)继承抽象类时,如果父类中有抽象方法,那么子类必须全部重写父类中的抽象方法


package com.example;

public abstract class Person{

	public abstract void p1(); // 抽象方法
public abstract void p2(); public void p3(){
System.out.println("Person: p3()");
}
public void p4(){
System.out.println("Person: p4()");
}
}

package com.example;

public class Man extends Person{
@Override
public void p1(){
} @Override
public void p2(){
}
}

七、final 关键字

1)由 final 关键字修饰的类不能被继承,例如:public final class Person{}

2)在继承关系中,如果父类的方法由 final 关键字修饰,那么该方法将不能被子类重写

参考资料:

《Java 编程思想》第4版


End~

Java笔记 —— 继承的更多相关文章

  1. Java笔记: 继承成员覆盖和隐藏

    在扩展类时,既可以向类中添加新的成员,也可以重新定义现有的成员.重定义现有成员的具体效果取决于成员的类型.本文不会详细的介绍概念,只简要总结覆盖(overriding,也叫重写)和隐藏(hiding) ...

  2. [Java笔记]继承

    继承只是继承框架,而数据没有继承!. 继承不改变父类数据!

  3. java学习笔记 --- 继承

    继承 (1)定义:把多个类中相同的成员给提取出来定义到一个独立的类中.然后让这多个类和该独立的类产生一个关系,    这多个类就具备了这些内容.这个关系叫继承.  (2)Java中如何表示继承呢?格式 ...

  4. Java学习笔记二十三:Java的继承初始化顺序

    Java的继承初始化顺序 当使用继承这个特性时,程序是如何执行的: 继承的初始化顺序 1.初始化父类再初始子类 2.先执行初始化对象中属性,再执行构造方法中的初始化 当使用继承这个特性时,程序是如何执 ...

  5. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  6. java笔记整理

    Java 笔记整理 包含内容     Unix Java 基础, 数据库(Oracle jdbc Hibernate pl/sql), web, JSP, Struts, Ajax Spring, E ...

  7. Java笔记 —— 方法重载和方法重写

    Java笔记 -- 方法重载和方法重写 h2{ color: #4ABCDE; } a{ text-decoration: none !important; } a:hover{ color: red ...

  8. Java笔记 —— 初始化

    Java笔记 -- 初始化 h2{ color: #4ABCDE; } a{ text-decoration: none !important; } a:hover{ color: red !impo ...

  9. [转载]Java中继承、装饰者模式和代理模式的区别

    [转载]Java中继承.装饰者模式和代理模式的区别 这是我在学Java Web时穿插学习Java设计模式的笔记 我就不转载原文了,直接指路好了: 装饰者模式和继承的区别: https://blog.c ...

随机推荐

  1. 树状数组 P3605 [USACO17JAN]Promotion Counting晋升者计数

    P3605 [USACO17JAN]Promotion Counting晋升者计数 题目描述 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者! 为了方便,把奶牛从 ...

  2. git 本地分支与远程分支相关操作记录

    1.远程分支中有新增分支,但自己的本地分支没有对应同步 git checkout -b [remote-branch-name] origin/[remote-branch-name] 2. 查看本地 ...

  3. shell操作字符串案例

    #!/bin/bash name="Shell" url="http://cxy.com/" str1=$name$url #中间不能有空格 str2=&quo ...

  4. python内存相关问题

    想要弄清楚内存相关的问题,就要理清楚:变量.内存地址.值之间的关系:1.程序里什么时候分配新的内存地址?答:1.定义一个变量,内存就开辟一个内存空间,分配一个内存地址. 特殊: 如:a=687 a=1 ...

  5. Mybatis-plus多表关联查询,多表分页查询

    学习plus真的觉得写代码真的越来越舒服了.昨天开始接触吧,只要学会了多表关联查询.plus就能随意搭配使用了. 关于怎么搭建的就自行了去研究了哦.这里直接进入主题. 我用的是springboot+m ...

  6. Camtasia Studio 8卸载不了解决方法

    Win7:%ProgramData%\TechSmith\Camtasia Studio 8\RegInfo.iniWinXP:”%AllUsersProfile%\Application Data\ ...

  7. Sqlite和mysql的区别及优缺点

    小型嵌入式,跟mysql差不多,但更小,功能也少,属于本地数据库多用于读多写少,100W以下的数据压力不大,不支持分布式.   真正开源的小型嵌入式数据库,支持事务.触发器器  性能至少优于mysql ...

  8. sp_executesq用法

    第一种用法: --@sqlstring :就是你要执行的sql语句字符串--@ParmDefinition: @sqlstring里边用到的参数在这里声明 输出的参数要加output --sp_exe ...

  9. Python import搜索路径相关

    import搜索路径 在当前目录下搜索该模块 在环境变量 PYTHONPATH 中指定的路径列表中依次搜索 在 Python 安装路径的 lib 库中搜索 查看当前的搜索路径 import sys p ...

  10. Mybatis学习笔记5 - 参数处理

    1.单个参数:mybatis不会做特殊处理,#{参数名}:取出参数值. 2.多个参数:mybatis会做特殊处理. 多个参数会被封装成 一个map, key:param1...paramN,或者参数的 ...