1.继承的概念
继承(inheritance)是面向对象的重要概念之一,它提供在已有类的
基础上创建新类,使新创建的类自动拥有被继承类的全部成员;是
构造可复用软件的有效机制。
父类/超类——被继承类;子类/派生类——继承产生的新类。
① 子类自动拥有父类的全部成员,包括成员变量和方法等
② 子类可以更改从父类继承来的成员,使成员适应新的需求;
③ 子类也可以增加自己的成员,使类的功能得以扩充。
④ 但是,子类不能删除父类的成员。
注:Java中仅仅支持单一继承,同时Java采用Interface(接口) 变相实现多重继承而避免父类二义性
2.Java继承的语法
在Java中实现继承需要使用到extends关键字;
实现继承的一般语法是:
[访问修饰符] class 派生类名 extends 基类名 {
成员列表
}
如:class Student extends Person
{
//实现Student类的数据成员和成员方法
}
3.子类对象的实例化
① 先调用父类的构造->调用父类构造函数
② 再调用子类构造->调用子类构造函数
③ 若子类中没有定义构造方法,则它自动地调用父类无参数的构造方法;若子类定义了构造方法,应该在第一条语句的位置调用父类的构造方法,否则系统将在此位置插入一条调用父类无参数构造方法的语句。
④ 对于父类中含有参数的构造方法,只能在子类的构造方法中利用super显式地调用。
eg1:子类对象初始化缺省调用父类构造函数
class Person {
private String name;
private int age;
public Person(){
System.out.println("父类Person的构造方法");
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
}
class Student extends Person{
private String school;
public Student(){
System.out.println("子类student构造方法");
}
}
public class testExtend {
public static void main(String args[]){
Student stu = new Student();
}
}
输出: 父类Person的构造方法
子类student构造方法
eg1:子类对象初始化显示调用父类构造函数
其余同上
public Student(){
super(); //显式调用父类无参数构造方法
System.out.println("子类student构造方法");
}
输出: 父类Person的构造方法
子类student构造方法
eg3:父类带参数的构造方法必须显示调用
其余同上
public Person(String name,int age){
this.name = name;
this.age = age;
System.out.println("父类Person的构造方法");
}
class Student extends Person{
private String school;
public Student(String name,int age,String school){
super(name,age); //显式调用父类构造方法
this.school = school;
System.out.println("子类student的构造方法");
}
}
public class testExtend {
public static void main(String args[]){
Student stu = new Student("张三",30,"山东大学");
}
}
输出: 父类Person的构造方法
子类student构造方法
eg4:父类有参数和无参的构造函数共存时,可隐式调用无参数构造函数
其余同上
public Person(){
System.out.println("父类无参数的构造方法");
}
public Person(String name,int age){
this.name = name;
this.age = age;
System.out.println(“父类带参数的构造方法");
}
class Student extends Person{
private String school;
public Student(String school){
this.school = school;
System.out.println("子类student的构造方法");
}
}
输出: 父类无参数的构造方法
子类student的构造方法
eg5:子类成员方法覆盖父类原型相同的成员方法
public Person(){
System.out.println("父类无参数的构造方法");
}
public Person(String name,int age){
this.name = name;
this.age = age;
System.out.println("父类带参数的构造方法");
}
class Student extends Person{
private String school;
public Student(String name,int age,String school){
super(name,age);
this.school = school;
System.out.println("子类带参数的构造方法");
}
public void print(){
System.out.println("调用子类的成员方法");
}
}
public class testExtend {
public static void main(String args[]){
Student stu = new Student(“张三”,20,“电子科大");
stu.print();
}
}
输出: 父类带参数的构造方法
子类带参数的构造方法
调用子类的成员方法
eg6:子类调用父类的成员方法使用super
public void print(){
super.print()
}
输出: 父类带参数的构造方法
子类带参数的构造方法
调用父类的成员方法
eg7:如果父类成员方法为private,则不能在子类调用
public void print(){
super.print() //调用父类私有成员是错误的
}
注:父类中的私有成员对子类是不可见的,只能通过父类public成员函数来访问
eg8:子类重写父类名字,限定符,参数都相同的成员方法
class Student extends Person{
private String school;
public Student(String name,int age,String school){
super (name,age);
this.school = school;
System.out.println("子类带参数的构造方法");
}
public void print(){ //(2)
System.out.println(“调用子类的成员方法");
}
}
public class testExtend {
public static void main(String args[]){
Student stu1 = new Student(“张明”,20, "电子科大");
Person stu2 = stu1; //父类引用引用子类对象
stu1.print();
stu2.print();
}
}
输出: 父类带参数的构造方法
子类带参数的构造方法
调用子类的成员方法
调用子类的成员方法
eg9:子类重写父类的私有成员时,不能用父类引用调用子类成员方法
private void print(){ //(1)
System.out.println("调用父类的成员方法");
}
public class testExtend {
public static void main(String args[]){
Student stu1 = new Student(“张明”,20, "电子科大");
Person stu2 = stu1; //父类引用引用子类对象
stu1.print();
stu2.print(); // 错误
}
}
原因: 父类的print()为私有的成员方法
eg10:子类重写父类的protected方法时,能用父类调用子类的该方法
protected void print(){ //(1)
System.out.println("调用父类的成员方法");
}
public class testExtend {
public static void main(String args[]){
Student stu1 = new Student(“张明”,20, "电子科大");
Person stu2 = stu1; //父类引用引用子类对象
stu1.print();
stu2.print(); //
}
}
输出: 父类带参数的构造方法
子类带参数的构造方法
调用子类的成员方法
调用子类的成员方法
eg11:父类引用不能调用子类新拓展的成员方法,如print()
//去掉父类的成员方法print()
public void print(){ //(2)
System.out.println(“调用子类的成员方法");
}
public class testExtend {
public static void main(String args[]){
Student stu1 = new Student(“张明”,20, "电子科大");
Person stu2 = stu1; //父类引用引用子类对象
stu1.print();
stu2.print(); // 错误
}
}
结论:
父类对象可引用子类实例;如 :Person p = new Student()
子类对象不能引用父类实例;如 Student s = new Person() 是错误的
如果使用父类对象引用子类实例,父类对象只能执行那些在父类中声明、被子类覆盖了的子类方法。
1.基本概念
多态性 (Polymorphism) 是指一个程序中同名的不同方法共存的情况,即一个程序中相同名字方法表示不同的实现。
表现在继承中为方法的重写(overwrite)。
① 多个子类从同一父类继承(extends)而来。
② 每个子类都重写了父类的某个方法。
③ 被重写的方法在不同的子类中有不同的形式。
④ 例:动物会叫,因此人、猫、狗都会叫,但表现形式不同。
表现在一个类中为方法的重载(override/overload)
①一个类有多个同名的方法,但这些方法的参数个数或类型不一样。
② 例:人吃不同的东西采用不同的形式。
2.多态的类型
1)编译时多态性
对于多个同名方法,如果在编译时能够确定执行同名方法中的哪一个,则称为编译时多态性。
方法的重载——都是编译时多态性。 方法的覆盖——表现出两种多态性,当对象引用本类实例时, 为编译时多态性;否则为运行时多态性。
2)运行时多态性
如果在编译时不能确定、只有在运行时才能确定执行多个同名方法中的哪一个,则称为运行时多态性。
3.子类重写父类成员方法
当子类从父类继承来的成员不能满足子类需要时,子类不能删除它们,但可以重定义它们,扩充父类成员方法的功能,使父
类成员能够适应子类新的需求。
子类重定义父类成员包括:
①重定义父类的成员变量,则隐藏父类的成员变量;
②重定义父类的成员方法,如果参数列表相同,则覆盖父类的成员方法,否则重载。
子类重定义父类成员表现出多态性,父类对象引用父类成员,子类对象引用子类成员。重定义的同名成员之间不会产生冲突和混乱。
4.成员方法的重写规则
① 参数列表必须完全与被重写方法的相同;
② 返回类型必须完全与被重写方法的返回类型相同;
③ 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的方法被声明为public,那么在子类中重写该方法就不能声明为protected。
④ 父类的成员方法只能被它的子类重写。
⑤ 声明为final的方法不能被重写。
⑥ 声明为static的方法不能被重写,但是能够被再次声明。
⑦ 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private和final的方法。
⑧ 子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和 protected的非final方法。
⑨ 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。
⑩ 构造方法不能被重写。
class Person {
public void f() {
System.out.println("父类");
}
}
class Students extends Person {
public void f() {
System.out.println("学生类");
}
}
class Teacher extends Person {
public void f() {
System.out.println("教师类");
}
}
public class TestExtend {
public static void main(String[] args) {
Person s = new Students();
Person t = new Teacher();
s.f();
t.f();
}
}
运行结果:
学生类
教师类
1.基本概念
1)抽象方法是一种特殊的方法,它只有声明而没有具体的实现,抽象方法必须用abstract关键字进行修饰;
2)如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用 abstract关键字修饰。
3)抽象类中可以有零个或多个抽象方法,也可以包含非抽象的方法。抽象类中可以没有抽象方法,有抽象方法的类必须是抽象类。
4)抽象类可以派生子类,在抽象类派生的子类中必须实现抽象类中定义的所有抽象方法。
5)抽象类不能创建对象,创建对象的工作由抽象类派生的子类来实现。
6)如果父类已有同名的abstract方法,则子类中就不能再有同名的抽象方法。
7)abstract不能与final并列修饰同一个类; 不能与private、static、final或 native并列修饰同一个方法。
2.抽象方法与抽象类的作用
抽象类—用于描述抽象的概念,其中声明的方法为多个子类约定方法声明,每个子类可以根据自身的实际情况,给出方法的具体实现,显然不同的子类可以有不同的方法实现。
抽象方法—用于声明方法的参数和返回值,具体实现由抽象类的子类完成,所有的子类必须实现父类的抽象方法.
提供方法声明与方法实现的分离机制,使得抽象类的多个不同的子类能够表现出共同的行为能力.
abstract class Shapes {
public int x, y;
public int width, height;
public Shapes(int x, int y, int width, int height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
abstract double getArea();
abstract double getPerimeter();
}
class Circle extends Shapes {
public double r;
public double getArea() {
return (r * r * Math.PI);
}
public double getPerimeter() {
return (2 * Math.PI * r);
}
public Circle(int x, int y, int width, int height) {
super(x, y, width, height);
r = (double) width / 2.0;
}
}
class Square extends Shapes {
public double getArea() {
return (width * height);
}
public double getPerimeter() {
return (2 * width + 2 * height);
}
public Square(int x, int y, int width, int height) {
super(x, y, width, height);
}
}
class Ellipse extends Shapes {
double a;
double b;
public double getArea() {
return (Math.PI * a * b);
}
public double getPerimeter() {
return (2*Math.PI * a+4 *( b-a));
}
public Ellipse(int x, int y, int width, int height) {
super(x, y, width, height);
if (width <= height) {
a = (double) width / 2.0;
b = (double) height / 2.0;
}
else {
b = (double) width / 2.0;
a = (double) height / 2.0;
}
}
}
public class TestShapeExtend {
public void display(Shapes p) {
System.out.println("面积:"+p.getArea()+" 周长:"+ p. getPerimeter() );
}
public static void main(String[] args) {
TestShapeExtend test = new TestShapeExtend();
Shapes c = new Circle(40,40,20,20);
Shapes s = new Square(40,40,20,30);
Shapes e= new Ellipse(40,40,20,30);
test. display(c);
test. display(s);
test. display(e);
}
}
输出:
面积:314.1592653589793 周长:62.83185307179586
面积:600.0 周长:100.0
面积:471.23889803846896 周长:82.83185307179586
abstract class Person{
private String name ; // 定义name属性
private int age ; // 定义age属性
public Person(String name,int age){
this.name = name ;
this.age = age ;
}
public String getName(){
return this.name ;
}
public int getAge(){
return this.age ;
}
public void say(){ // 人说话是一个具体的功能
System.out.println(this.getContent()) ; // 输出内容
}
public abstract String getContent() ; // 抽象方法,由子类决定
}
class Student extends Person{
private float score ;
public Student(String name,int age,float score){
super(name,age) ; // 调用父类中的构造方法
this.score = score ;
}
public String getContent(){
return "学生信息 --> 姓名:" + super.getName() + ";年龄:" + super.getAge() + ";成绩:" + this.score ;
}
}
class Worker extends Person{
private float salary ;
public Worker(String name,int age,float salary){
super(name,age) ; // 调用父类中的构造方法
this.salary = salary ;
}
public String getContent(){
return "工人信息 --> 姓名:" + super.getName() + ";年龄:" + super.getAge() + ";工资:" + this.salary ;
}
}
public class AbstractClassDemo {
public static void main(String args[]){
Person per1 = null ; // 声明Person对象
Person per2 = null ; // 声明Person对象
per1 = new Student("张三",20,99.0f) ; // 学生是一个人
per2 = new Worker("李四",30,3000.0f) ; // 工人是一个人
per1.say() ; // 学生说学生的话
per2.say() ; // 工人说工人的话
}
}
运行结果:
学生信息 --> 姓名:张三;年龄:20;成绩:99.0
工人信息 --> 姓名:李四;年龄:30;工资:3000.0
1.声明最终类
使用关键字final声明的类,不能被继承.
public final class Math extends Object //数学类,最终类
public class MyMath extends Math //编译错,最终类不能被继承
注意:抽象类不能被声明为最终类.
2.声明最终方法
使用关键字final声明的成员方法,不能被子类覆盖.
public class Circle1 extends Ellipse //圆是一种特殊的椭圆
{
private double radius; public final double area() //最终方法,不能被子类覆盖
{
return Math.PI*this.radius*this.radius;
}
}
注意:最终类中包含的都是最终方法,非最终类也可以包含最终方法。
见此篇
1.基本概念
① 内部类提供了更好的封装,只有外部类能访问内部类
② 内部类中的属性和方法即使是外部类也不能直接访问,相反内部类可以直接访问外部类的属性和方法,即使private.
③ 内部类可以独立继承接口,不受外部类是否继承接口影响
public class OuterClass {
private String outerName;
private int outerAge;
public class InnerClass{
private String innerName;
private int innerAge;
}
}
eg1:内部类可以直接访问外部类的元素,但是外部类不可以直接访问内部类的元素
public class OuterClass {
private String outerName;
private int outerAge;
public class InnerClass{
private int innerName;
InnerClass(){ //内部类可以访问外部类的元素
outerName="I am outer class";
outerAge=23;
}
public void display(){
System.out.println(outerName+" and my age is "+outerAge);
}
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.display();
}
}
eg2:内部类可以通过OuterClass.this来获得外部类的引用
public class OuterClass {
public void display(){
System.out.println("this is OuterClass...");
}
public class InnerClass{ //获取外部类的引用
public OuterClass getOuterClass(){
return OuterClass.this;
}
public void innerDisplay(){ //内部类也可以通过外部类的引用访问外部元素
getOuterClass().display();
}
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.innerDisplay();
}
}
eg3:外部类可以通过内部类引用间接访问内部类元素
public class OuterClass {
public void display(){ //外部类访问内部类元素,需要通过内部类引用访问
InnerClass innerClass=new InnerClass();
innerClass.innerDisplay();
}
public class InnerClass{
public void innerDisplay(){
System.out.println("I am inner class");
}
}
public static void main(String[] args) {
OuterClass outerClass=new OuterClass();
outerClass.display();
}
}
2.成员内部类
① 成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有成员属性和方法, 尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
② 在成员内部类中要注意两点:
第一:成员内部类中不能存在任何static的变量和方法;
第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
public class OuterClass {
private String str;
public void outerDisplay(){
System.out.println("outerClass...");
}
public class InnerClass{
public void innerDisplay(){
str= " innerClass..."; //使用外围内的属性
System.out.println(str);
outerDisplay(); //使用外围内的方法
}
}
public InnerClass getInnerClass(){
return new InnerClass();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.getInnerClass();
inner.innerDisplay();
}
}
3.局部内部类——成员方法中定义内部类
① 它是嵌套在方法和作用于内的,对于这个类的使用主要是应用于解决比较复杂的问题,想创建一个类来辅助外部类,又不希望这个类是公共可用的,所以就产生了局部内部类;
② 局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就会失效。
class OutClass {
private int age = 12;
public void Print(final int x) {
class InnerClass {
public void inPrint() {
System.out.println(x);
System.out.println(age);
}
}
new InnerClass().inPrint();
}
}
public class Demo {
public static void main(String[] args) {
OutClass out = new OutClass();
out.Print(3);
}
}
4.匿名内部类——没有名字的内部类
public class OuterClass {
public InnerClass getInnerClass(final int num,String str2){
return new InnerClass(){
int number = num + 3;
public int getNumber(){
return number;
}
}; /* 注意:分号不能省 */
}
public static void main(String[] args) {
OuterClass out = new OuterClass();
InnerClass inner = out.getInnerClass(2, "Uestc");
System.out.println(inner.getNumber());
}
}
interface InnerClass {
int getNumber();
}
代码解释:
① 匿名内部类是没有访问修饰符的。
② new 匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错。
③ 注意:匿名内部类是没有构造方法;因为它连名字都没有何来构造方法。
5.静态内部类
① Static可以修饰成员变量、方法、代码块,其他它还可以修饰内部类,使用static修饰的内部类称之为静态内部类;
② 静态内部类与非静态内部类之间最大的区别,非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。
③ 静态内部类的创建是不需要依赖于外围类。
④ 静态内部类不能使用任何外围类的非static成员变量和方法。
public class OuterClass {
private String sex;
public static String name = "Outer";
//静态内部类
static class InnerClass1{ /* 在静态内部类中可以存在静态成员 */
public static String _name1 = "Inner1_static";
public void display(){
System.out.println("OutClass name :" + name);
}
}
// 非静态内部类
class InnerClass2{
public String _name2 = "Inner2_name";
public void display(){
System.out.println("OuterClass name:" + name);
}
}
public void display(){
System.out.println(InnerClass1._name1);
new InnerClass1().display();
/* 非静态内部的创建需要依赖于外围类 */
OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
/* 非静态内部类的成员需要使用非静态内部类的实例 */
System.out.println(inner2._name2);
inner2.display();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.display();
}
}
运行结果:
Inner1_static
OutClass name :Outer
Inner2_name
OuterClass name:Outer