上次带大家学习了java里面比较重要的知识点类和对象,而且我们知道java是一门面向对象的语言,有时一个程序里可能有很多类,那么这么多类他们之间有什么联系吗?今天就带大家学习一下java类之间的关系。
什么是继承:
我们在日常学习中,在java程序编写时,先创建类,在实例化对象进一步对类进行使用,然而,世界上很多事物往往都不是独立的,他们之间可能存在着千丝万缕的联系,那么如何建立这种联系呢
举个例子创建一个狗类:
//Dog类
public class Dog{
String name;
String sex;
int age;
public void bark(){
System.out.println(name+"正在汪汪汪");
}
public void sleep(){
System.out.println(name+"正在睡觉")
}
}
在定义一个猫类:
public class Cat{
string name;
int age;
string color;
public void sleep()
{
System.out.println(name + "正在睡觉");
}
void mew(){
System.out.println(name + "喵喵喵~~~");
}
}
通过这两个例子我们可以很直观的看到,这两类有太多一样的地方,我们如果可以把这些一样的部分抽离出来,使用时直接调用它,代码效率会大大加强,实现了代码的复用,其实,这就是继承的本质。
继承 (inheritance) 机制 :是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性 的基础上进行扩展,增加新功能 ,这样产生新的类,称 派生类 。继承呈现了面向对象程序设计的层次结构, 体现了 由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码用 。例如:狗和猫都是动物,那么我们就可以将共性的内容进行抽取,然后采用继承的思想来达到共用。
public class Animal{
String name;
int age;
public void eat(){
System.out.println(name+"正在吃饭");
}
public void sleep(){
System.out.println(name+"正在睡觉");
}
}
public class Dog extends Animal{
void bark(){
System.out.println(name + "汪汪汪~~~");
}
}
public class Cat extends Animal{
void mew(){
System.out.println(name + "喵喵喵~~~");
}
}
我们将Dog类和Cat类称为子类,Animal为父类。
这样的话,代码就大大简化当我们再去创建动物对象时,就可以直接调用这里的Animal类中的成员,这里的extends是子类继承父类成员时必须使用的关键字。
子类如何访问父类的成员:
子类访问父类的成员,这里分为两种情况:
- 子类成员与父类成员名字一致。
- 子类成员与父类成员名字不一致。
子类成员与父类成员名字一致
public class Base {
int a;
int b;
int c;
}
/
public class Derived extends Base{
int a; // 与父类中成员a同名,且类型相同
char b; // 与父类中成员b同名,但类型不同
public void method(){
a = 100; // 访问父类继承的a,还是子类自己新增的a?
b = 101; // 访问父类继承的b,还是子类自己新增的b?
c = 102; // 子类没有c,访问的肯定是从父类继承下来的c
// d = 103; // 编译失败,因为父类和子类都没有定义成员变量b
}
}
这里需要注意的是:
- 当父类与子类成员名字一致时,优先访问自己的成员变量。
- 当访问成员变量时优先访问自己的,若没有再去访问父类的
- 若子类与父类都没有,会发生编译错误。
成员变量是这样,成员方法也是这样,调用成员方法时优先考虑子类自己的,若没有则去父类中查找,都没有就会报错。
public class Base {
public void ways1(){
System.out.println("Base中的ways1()");
}
public void ways2(){
System.out.println("Base中的ways2()");
}
}
public class Derived extends Base{
public void ways1(int a) {
System.out.println("Derived中的way1(int)方法");
}
public void ways2(){
System.out.println("Derived中的ways2()方法");
}
public void ways3(){
ways1(); // 调用的是父类中的因为,父类中午参数
ways1(20); // 传递参数,访问子类中的way1(int)
ways2(); //默认访问子类中的ways2
}
}
super关键字
上面我们说了,当访问的成员或变量子类与父类同名时,默认访问的是子类的,那么如果就像访问父类的呢?这里就需要使用到super关键字了。他可以在子类方法中访问父类的成员
class Country {
String name;
void value() {
name = "China";
}
}
class City extends Country {
String name;
void value() {
name = "Shanghai";
super.value(); //调用父类的方法
System.out.println(name); //默认打印子类中的china
System.out.println(super.name); //使用super关键字调用父类中的name
}
public static void main(String[] args) {
City c=new City();
c.value();
}
}
运行结果: Shanghai China
注意:
- super关键字只可以在非静态的方法中访问。
- super关键字是在子类中调用父类成员使用的。
子类的构造方法
我们在实例化子类对象时,会调用子类的构造方法,然而其实子类的构造方法在调用之前,编译器已经偷偷地将父类的构造方法调用过一次了,只是没有显示而已,我们可以这样理解,子类中的成员变量,其实是由两部分构成的,一部分是自己的,另一部分是从父类中继承下来的,所以必须先要把从父类中继承的成员变量初始化了,然后才初始化自己的成员变量。
正所谓:父子父子,先有父才能有子。
public class Base {
public Base(){
System.out.println("Base()");
}
}
public class Derived extends Base{
public Derived(){
// super();
System.out.println("Derived()");
}
}
public class Test {
public static void main(String[] args) {
Derived d = new Derived();
}
}
运行结果:Base() Derived()
总结:
- 当父类中显式定义无参或者默认的构造方法,那么在子类中都默认调用了父类无参数的构造方法。
- 当自行定义有参数的父类构造方法时,子类的构造方法中不在默认提供调用父类构造方法的操作。需要用户在子类的构造方法中自行调用,否则编译错误。
- 当在子类中调用super(),必须将super()放到第一行,否则编译错误。
- 在子类的构造方法中不能同时出现super()和this因为从语法上他们都要放到第一行。
super和this的区别
这里提醒一下,不懂this的uu们,看我的上一篇文章。
this 相当于是指向当前对象本身,可以理解为指向当前对象的指针,(当然java中没有指针只是这样方便理解)。
这里介绍this的几种用法:
- .普通的直接引用:直接使用this.×××来引用
- 当类中的成员变量与成员函数中的参数同名时,this.成员变量,用来指代当前对象的引用
class Person { private int age = 10; public Person(){ System.out.println("初始化年龄:"+age); } public int GetAge(int age){ this.age = age; //调用的是当前对象Harry的age=形参的age return this.age; } } public class test1 { public static void main(String[] args) { Person Harry = new Person(); System.out.println("Harry's age is "+Harry.GetAge(12)); } }
- 用来调用另一种构造方法:
public class Chinese { Chinese() { System.out.println("无参数构造方法"": "+"A chinese coder."); } Chinese(String name) { System.out.println("含一个参数的构造方法"": "+"his name is " + name); } Chinese(String name, int age) { this(name);// 调用具有相同形参的构造方法(3) System.out.println("两个形参的构造方法:his age is " + age); } public static void main(String[] args) { Chinese cn = new Chinese(); cn = new Chinese("codersai"); cn = new Chinese("codersai", 18); } }
super可以理解为一个指向父类对象的指针,完成对父类对象的引用。
普通的直接引用:super.×××引用父类对象的成员变量
子类中的成员变量或方法与父类中的成员变量或方法同名:使用super.×××来调用父类的成员。
引用构造函数:子类中的构造方法执行之前要先调用父类中的super(),且必须是第一行。
初始化代码运行顺序
静态代码块执行 实例代码块执行 构造方法执行
这三种代码块的运行顺序是怎么样的呢,
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person:构造方法执行");
}
{
System.out.println("Person:实例代码块执行");
}
static {
System.out.println("Person:静态代码块执行");
}
}
class Student extends Person{
public Student(String name,int age) {
super(name,age);
System.out.println("Student:构造方法执行");
}
{
System.out.println("Student:实例代码块执行");
}
static {
System.out.println("Student:静态代码块执行");
}
}
public class TestDemo4 {
public static void main(String[] args) {
Student student1 = new Student("张三",19);
System.out.println("===========================");
Student student2 = new Student("ningzhiyuan",20);
运行结果:
Person :静态代码块执行Student :静态代码块执行Person :实例代码块执行Person :构造方法执行Student :实例代码块执行Student :构造方法执行===========================Person :实例代码块执行Person :构造方法执行Student :实例代码块执行Student :构造方法执行
由此我们知道了代码块的执行顺序:父类静态代码块,子类静态方法,父类实例代码块 ,父类构造方法,子类实例代码块,子类构造方法。并且静态代码块只执行一次。
访问限定符
java中为了更好的封装,建立了访问限定符,让数据封装起来,数据更加安全。
这些限定符决定了数据在哪里可以使用,哪里不可以使用,对于数据权限的管理非常有效。也提醒我们后面使用数据之前应该了解数据的权限避免写出错误的代码。