Java继承
概念
继承是java面向对象编程技术的一块基石,因为它允许你创建分等级层次的类
继承就是子类继承父类的行为和特征,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类的相同的行为
如:兔子属于食草动物,狮子属于肉食动物,兔子和狮子都是动物
继承要满足的关系
如果 b is-a,则可说明b继承于a,父类更通用,子类更具体
类的继承格式
class{
父类具体内容;
}
class 子类 extends 父类{
子类具体内容;
}
举个例子:
企鹅类
public class Penguin {
private String name;
private int id;
public Penguin(String myName, int myid) {
name = myName;
id = myid;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void sleep(){
System.out.println(name+"正在睡");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}
老鼠类
public class Mouse {
private String name;
private int id;
public Mouse(String myName, int myid) {
name = myName;
id = myid;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void sleep(){
System.out.println(name+"正在睡");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}
两断代码可以看出,代码存在重复,导致后果是代码大量臃肿,而且维护性不高(后期修改需要改动大部分,且容易遗漏),继承就是为了解决这个问题而产生的。
动物类:
public class Animal {
private String name;
private int id;
public Animal(String myName, int myid) {
name = myName;
id = myid;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void sleep(){
System.out.println(name+"正在睡");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}
这个animal类作为企鹅类和老鼠类共有的部分,所以可以通过继承,代码变得更加简洁,提高了代码的复用性,也便于后期维护。
企鹅类
public class Penguin extends Animal {
public Penguin(String myName, int myid) {
super(myName, myid);
}
}
老鼠类
public class Mouse extends Animal {
public Mouse(String myName, int myid) {
super(myName, myid);
}
}
注:通过super关键字,子类直接继承父类方法
继承类型
单继承
class A{ }
class B extends A{ }
多重继承
class A{ }
class B entends A{ }
class C entends B{ }
不同类继承同一个类
class A{ }
class B entends A{ }
class C entends A{ }
注意java不同于python,java不支持多继承,即一个子类继承至多个父类
继承的特性
- 子类拥有父类非peivate的方法
- 子类可以拥有自己的属性和方法,即子类可以对父类方法进行扩展
- 子类可以用自己的方式实现父类的方法
- Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性
- 提高了类之间的耦合性(继承的缺点,耦合性高就会造成代码块之间的联系紧密,代码独立性差)
继承关键字
在java中,类的继承都是单一继承的,也就是说,一个子类只能拥有一个父类,所以extends只能继承一个类
public class Animal {
private String name;
private int id;
public Animal(String myName, String myid) {
//初始化属性值
}
public void eat() { //吃东西方法的具体实现 }
public void sleep() { //睡觉方法的具体实现 }
}
public class Penguin extends Animal{
}
implements关键字
使用implements关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口也接口之间采用逗号分隔)
public interface A {
public void eat();
public void sleep();
}
public interface B {
public void show();
}
public class C implements A,B {
}
super和this关键字
super关键字:我们可以使用super关键字来实现父类成员的访问,用来引用对象的父类
this关键字:指向成员变量之间的引用
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
结果为:
animal : eat
dog : eat
animal : eat
final关键字
final关键字声明可以把类定义为不能被继承的类,即最终类;或者用于修饰方法,该方法不能被继承的子类重写:
声明类:
final class 类名{ }
声明方法:
(public/private/default/pritected)final 返回值类型 方法名(){ }
注:实例变量也可以被定义为final,被定义为final的变量不能被修改。被声明为final的类的方法也自动被声明为final
构造器
子类是不继父类的构造器的(构造方法或构造函数)的,它只是调用(隐式或显式)。如果父类的构造函数带有参数,则必须在子类的构造器中显示地通过super关键字调用父类的构造函数并配以适当的参数列表
如果父类构造器没有带有参数,而在子类的构造器中不需要使用super关键字调用父类构造器,系统会自动用父类的无参构造器
class SuperClass {
private int n;
SuperClass(){
System.out.println("SuperClass()");
}
SuperClass(int n) {
System.out.println("SuperClass(int n)");
this.n = n;
}
}
// SubClass 类继承
class SubClass extends SuperClass{
private int n;
SubClass(){ // 自动调用父类的无参数构造器
System.out.println("SubClass");
}
public SubClass(int n){
super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass(int n):"+n);
this.n = n;
}
}
// SubClass2 类继承
class SubClass2 extends SuperClass{
private int n;
SubClass2(){
super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass2");
}
public SubClass2(int n){ // 自动调用父类的无参数构造器
System.out.println("SubClass2(int n):"+n);
this.n = n;
}
}
public class TestSuperSub{
public static void main (String args[]){
System.out.println("------SubClass 类继承------");
SubClass sc1 = new SubClass();
SubClass sc2 = new SubClass(100);
System.out.println("------SubClass2 类继承------");
SubClass2 sc3 = new SubClass2();
SubClass2 sc4 = new SubClass2(200);
}
}
结果为:
------SubClass 类继承------
SuperClass()
SubClass
SuperClass(int n)
SubClass(int n):100
------SubClass2 类继承------
SuperClass(int n)
SubClass2
SuperClass()
SubClass2(int n):200
java重写(Override)
概念:
重写是子类对于父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。即外壳不变,核心重写。子类可以根据自己特有的行为,来定义更加具体的属于自己的行为方式。重写方法都不能抛出新的检查异常或比被重写的方法申明更加宽泛的异常
如:
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
class Dog extends Animal{
public void move(){
System.out.println("狗可以跑和走");
}
public void bark(){
System.out.println("狗可以吠叫");
}
}
public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
b.bark();
}
}
结果为:
TestDog.java:30: cannot find symbol
symbol : method bark()
location: class Animal
b.bark();
方法重写的规则
- 参数列表必须完全和被重写的方法相同
- 返回类型与被重写的方法的返回类型可以不相同,但是必须是父类返回值的派生类
- 访问权限不能比父类中被重写的方法的访问权限更低
- 父类的成员方法只能被它的子类重写
- 声明为final的方法不能被它的子类重写
- 声明为static的方法不能被重写,但是可以被再次声明
- 子类和父类在同一个包中,那么子类可以重写父类的所有方法,处理声明为private和final的方法
- 子类和父类不在同一个包中,那么子类只能够重写父类声明为public和protected的非final方法
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以
- 构造方法不能重写
- 如果不能继承一个方法,则不能重写这个方法
方法的重载
概念:
重载是在同一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同
每个重的方法(或构造函数)都必须有一个独一无二的参数型列表
最常用到重载的地方就是构造器
重载规则
- 被重载的方法必须改变参数列表(参数个数或者类型不一样)
- 被重载的方法可以改变返回类型
- 被重载的方法可以改变访问修饰符
- 被重载的方法可以声明新的或更广 的检查异常
- 方法能够在同一个类中或者在一个子类中重载
- 无法以返回值类型作为判断重载函数的区分标准
重写与重载的区别
java多态
概念:
多态是同一个行为具有不同的表现形式或形态的能力
多态就是一个接口,使用不同的实例而执行不同操作
多态的优点 - 消除事物之间的耦合关系(解决继承的不足)
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
多态存在的条件 - 继承
- 重写
- 父类引用指向子类对象
如:
Parent p = new Child();
在使用多态方式调用方法时,应该检查父类是否含有该方法,如果没有,则编译错误,如果有,再去调用子类的同名方法。这样使得程序有良好的扩展,并可以对所有的对象进行通用处理。
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
结果为:
吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠
多态的实现方式
方式一:重写
方式二:接口
方式三:抽象类和抽象方法
抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类不能用来实例化对象,但是抽象的类可以被继承。
父类包含了子类集合的常见方法,但是由于父类本身是抽象的,所以不能使用这些方法
在java中,抽象类用来表示一种继承关系,一个类是只能继承一个抽象类,而一个类却可以实现多个接口。
在java中,使用abstract class来定义抽象类
继承抽象类
定义一个抽象类
public abstract class Employee
{
private String name;
private String address;
private int number;
public Employee(String name, String address, int number)
{
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public double computePay()
{
System.out.println("Inside Employee computePay");
return 0.0;
}
public void mailCheck()
{
System.out.println("Mailing a check to " + this.name
+ " " + this.address);
}
public String toString()
{
return name + " " + address + " " + number;
}
public String getName()
{
return name;
}
public String getAddress()
{
return address;
}
public void setAddress(String newAddress)
{
address = newAddress;
}
public int getNumber()
{
return number;
}
}
继承抽象类
public class Salary extends Employee
{
private double salary; //Annual salary
public Salary(String name, String address, int number, double
salary)
{
super(name, address, number);
setSalary(salary);
}
public void mailCheck()
{
System.out.println("Within mailCheck of Salary class ");
System.out.println("Mailing check to " + getName()
+ " with salary " + salary);
}
public double getSalary()
{
return salary;
}
public void setSalary(double newSalary)
{
if(newSalary >= 0.0)
{
salary = newSalary;
}
}
public double computePay()
{
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
}
执行函数
public class AbstractDemo
{
public static void main(String [] args)
{
Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
System.out.println("Call mailCheck using Salary reference --");
s.mailCheck();
System.out.println("\n Call mailCheck using Employee reference--");
e.mailCheck();
}
}
结果为:
Constructing an Employee
Constructing an Employee
Call mailCheck using Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0
Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.
抽象方法
如果你设计一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,则该方法可以为在父类声明的一个抽象方法。
抽象方法同样使用关键字abstract来修饰
public abstract class Person
{
private String name;
private String address;
private int number;
public abstract double computePay();
//其他代码块
}
注意:
- 如果一个类包含抽象方法,那么该类一定是一个抽象类
- 任何子类必须重写父类的抽象方法,或者声明自身也为抽象类
继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象。
抽象类规定 - 抽象类不能被实例化,如果被实例化,就会报错,无法通过编译。只有抽象类的非抽象子类才能实例化对象
- 抽象类不一定包含抽象方法,但是有抽象方法的类一定是抽象类
- 抽象类中的抽象方法只是声明,不包含方法体,就是不用给出方法的具体实现也就是方法的具体功能
- 构造方法,类方法不能声明为抽象方法
- 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类
Java封装
封装解释:
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
如果要访问该类的代码和数据,必须通过严格的接口控制
封装最主要的功能在于我们能够修改自己的实现代码,而不用修改那些调用我们代码的程序片段
适当的封装可以让程序代码更加容易理解与维护,也能够加强代码的安全性
优点: - 类内部的结构可以自由修改
- 良好的封装能够减少耦合
- 可以对成员变量进行更精确的控制
- 隐藏信息,实现细节
实现封装的方法步骤
1.修改属性的可见性来限制对属性的访问(一般为private)
public class Person{
private String name;
private int age;
}
将姓名和年龄设置为私有的,只有本类才能访问
2.对每个属性值提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问
public class Person{
private String name;
private int age;
public int getAge(){
return age;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public void setName(String name){
this.name = name;
}
}
Java接口(interface)
概念:
接口是一个抽象类型,是java中抽象方法的集合,接口通常以interface来声明。一个类可以通过继承接口的方式,来实现接口的抽象方法
注意:接口不是类,但是编写接口与类相似。类描述对象的属性和方法,接口包含要实现的方法。
接口无法被实例化,只能被实现,一个实现接口的类,必须实现接口内所描述的所有方法,否则该类就必须声明为抽象类
接口与类相似点 | 接口与类区别 |
---|---|
一个接口可以实现多个方法 | 接口不能用于实例化对象 |
接口保存在.java文件中,文件名使用接口名 | 接口没有构造方法 |
接口的字节码文件保存在.class结尾的文件中 | 接口中所有的方法必须是抽象方法 |
接口向对应的字节码文件必须在包名称相匹配的目录结构 | 接口不包含成员变量,除了static和fianl变量 |
接口不能被类继承而是被类实现 | |
接口支持多继承 |
接口特性
-
接口中每一个方法都隐式声明为public abstrict
-
接口可以包含变量,但是接口中的变量会被隐式的声明为public static final
-
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法
抽象类与接口的区别 -
抽象类的方法可以有方法体,但是接口不行
-
抽象类的成员变量可以各式各样,但是接口的成员变量只能是public static final
-
一个类只能继承一个抽象类,但是却可以实现多个接口注意类是继承,接口是实现,是实现
-
接口中不能包含静态代码块以及静态方法,而抽象类却可以。
接口的声明
[可见度] interface 接口名称 [extends 其他接口名]{
//声明变量(public static final)
//抽象方法 (public abstract)
}
接口的实现
类使用implements关键字实现接口
类名 implements 接口名 [,其它接口名,其它接口名...,....]{
}
接口的继承
一个接口可以继承另一个接口,和类之间的继承方式比较相似,使用extends关键字来实现接口的继承
如果一个接口继承了多个接口不同于类,接口多继承是合法的,则继承的接口之间应该用逗号分隔。
~
~
~
更多知识请关注菜驿站Java