一、成员变量与局部变量的对比
1、在类中定义的位置不同
(1)成员变量:在类中定义
(2)局部变量:在方法体(包括代码块)或方法的参数列表中定义
2、权限修饰不同
(1)成员变量:一定使用了某种访问权限修饰符进行修饰,可以使用static修饰符进行修饰
(2)局部变量:一定没有权限修饰符进行修饰,不可以使用static修饰符进行修饰
3、初始化不同:
(1)成员变量:
非静态成员变量:在创建对象后,由构造方法进行初始化
静态成员变量:在类加载时,由编译器进行初始化
对于未初始化的非静态成员变量,JVM会在创建对象时进行默认初始化,
对于未初始化的静态成员变量,JVM会在类加载时进行默认初始化
(2)局部变量
局部变量在声明后,如果要使用,使用前必须程序员必须对其进行初始化
4、生命周期不同
(1)成员变量:
时间 | 生(创建) | 死(销毁) |
---|---|---|
非静态变量 | 创建对象时 | 对象被垃圾回收站回收时 |
静态变量 | 类加载时 | 类被卸载时 |
(2)局部变量:
时间 | 生(创建) | 死(销毁) |
---|---|---|
局部变量 | 方法被调用时 | 方法执行完成时 |
5、在内存中的位置不同
(1)成员变量
位置 | |
---|---|
非静态变量 | 与对象一同在堆空间中存储 |
静态变量 | 与类信息一同在方法区中存储 |
(2)局部变量
位置 | |
---|---|
局部变量 | 与方法一同在栈空间中存储 |
二、面向对象语言的特征-继承(extends)
1、定义:继承是面向对象语言的三大特征之一,正如儿子可以继承父亲的财产一样,子类可以继承父类中的属性和功能
2、使用时机:
(1)当多个类存在相同属性和相同行为时,可以将这些内容单独抽取到一个类中,那么,多个类中无需再定义这些属性和行为,只需要和抽取出来的类构成继承关系即可
(2)满足什么是什么
3、语法:
通过extends关键字,可以声明一个类B继承自另一个类A,定义格式如下:
【修饰符】class 类A{
}
【修饰符】class 类B extends 类A{
}
类B,称为子类/派生类
类A,称为父类/基类
4、优点:
(1)减少代码冗余,提高了代码的复用性
(2)提高了程序功能的扩展性(可在猫类和狗类中定义他们独有的属性和行为)
(3)继承的出现让类与类之间产生了is-a关系,为多态的使用提供了前提
5、案例一:猫、狗、动物
(1)描述
猫和狗都是动物,他们都有名字、年龄,都吃东西,且猫会抓老鼠,狗会看家
猫是动物,狗是动物
(2)定义类
于是可以抽取出一个动物类,单独定义名字和年龄属性以及吃东西功能
并且将猫类与狗类与动物类构成继承关系
(3)代码实现
package com.ffyc.bcms.dao;
public class Animal {
private String name;
private int age;
public void eat(){
System.out.println("动物吃东西");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.ffyc.bcms.dao;
public class Cat extends Animal{
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
Dog dog=new Dog();
dog.setName("旺财");
dog.setAge(2);
System.out.println(dog.getName()+":"+dog.getAge());
dog.eat();
dog.lookHome();
Cat cat=new Cat();
cat.setName("丽丽");
cat.setAge(1);
System.out.println(cat.getName()+":"+cat.getAge());
cat.eat();
cat.catchMouse();
}
}
6、继承的细节
(1)子类会继承父类所有的实例变量和实例方法(包括所有的非静态的成员变量和成员方法,也包括所有静态的成员变量和成员方法,不包括构造方法)
package com.ffyc.bcms.dao;
public class Animal {
protected int num=0;
private String name;
private int age;
public static int staticNum;
public static void print(){
System.out.println("动物类中的静态方法");
}
public void eat(){
System.out.println("动物吃东西");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
Dog dog=new Dog();
// 继承动物类中的静态方法
dog.print();
// 继承动物类中的静态变量
System.out.println(dog.staticNum);
}
}
(2)子类不能直接访问父类中私有的成员变量和成员方法
(3)在Java中,继承的关键字是"extends",表示子类是对父类的扩展
(4)Java支持类多层继承的继承体系
package com.ffyc.bcms.dao;
public class XTQ extends Dog{
public void fly(){
System.out.println("哮天犬会飞");
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
XTQ xtq=new XTQ();
xtq.setName("啸天");
xtq.setAge(3);
System.out.println(xtq.getName()+":"+xtq.getAge());
xtq.eat();
System.out.println(xtq.staticNum);
xtq.print();
xtq.lookHome();
xtq.fly();
}
}
(5)Java支持单继承,不支持多继承。一个类只能直接继承一个类,不能直接继承多个类
(6)Java中如果一个类如果没有使用extends关键字显示地继承其他类,那么这个类会默认继承自Object类(全类名为java.lang.Object)。Object类是Java类层次结构中的根类,是所有其他类(无论是自定义类还是默认类)的父类,每个类都直接或间接地继承自Object类。每个类都使用Object类作为超类
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
XTQ xtq=new XTQ();
System.out.println(xtq.hashCode());// 调用Object类中的hashCode()方法
}
}
(7)一个父类可以同时拥有多个子类,一个子类也可以直接或间接拥有多个父类
(8)继承后的成员变量和成员方法的访问权限是以父类所在位置为基准的
三、方法的重写(Override)
1、定义:子类对从父类中继承来的某个方法进行改造,在程序执行调用此方法时,子类中改造后的方法会覆盖掉父类中改造前方法,我们将这一过程称为方法的重写(也可称为方法的覆盖)
2、使用时机:当父类中方法的实现(父类方法中为完成该方法所提供的功能而编写的代码)不能满足子类中对该方法的需要时(子类中对该方法的实现与父类不同),则在子类中对该方法进行重写(在子类中对该方法进行重写定义,方法结构与父类中的方法结构除访问权限修饰符外必须相同)
3、重写对象:在子类中只能对从父类中继承过来的非静态方法进行重写
4、必须满足的规则:
(1)子类重写的方法必须与父类被重写的方法的方法名相同且参数列表相同(即参数的数目和对应位置上参数的类型必须相同)
(2)子类重写的方法的返回值类型必须与父类中被重写的方法的返回值类型保持一致
(3)子类重写的方法的访问权限必须等于或大于父类中被重写方法的访问权限
注意:
子类从父类继承来的非成员变量方法中的私有方法和跨包的默认权限的方法都不能进行重写
(4)子类重写方法中抛出的异常的类型必须是父类被重写方法中抛出的异常的类型的子集
5、案例
狗和动物都吃东西,但狗有自己独特的吃东西的过程
因此要在狗类中对动物类中的eat()方法进行重写
(1)情况1:重写方法和被重写方法均由public修饰
package com.ffyc.bcms.dao;
public class Animal {
public void eat(){
System.out.println("动物吃东西");
}
}
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
public void eat(){
System.out.println("狗吃骨头");
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
Dog dog=new Dog();
dog.eat();
}
}
(2)情况2:重写方法拥有公共权限,被重写方法拥有默认权限
package com.ffyc.bcms.dao;
public class Animal {
void eat(){
System.out.println("动物吃东西");
}
}
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
public void eat(){
System.out.println("狗吃骨头");
}
}
package com.ffyc.bcms.service;
import com.ffyc.bcms.dao.Dog;
public class Test {
public static void main(String[] args) {
Dog dog=new Dog();
dog.eat();
}
}
6、@Override
(1)定义:@Override是在Java中定义的注解标签
(2)作用:写在方法上面,表示此方法是由父类中方法重写而来的,用来检测重写方法是否满足重写方法的规则
(3)优点:
优点一:在重写方法上加上@Override注解标签后编译器在预编译期间会对该重写方法的语法进行检测,可以帮助我们检查重写方法的格式是否正确
优点二:加上@Override注解标签的方法可以让阅读源代码的程序员清晰地知道这是一个重写的方法
(4)生成方式:可以手动编写,也可以使用工具自动生成
(5)注意:
- 即使重写方法没有这个注解标签,只要重写方法满足重写方法应该满足的规则,依旧是正确的重写方法
使用@Override注解标签的成员方法
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
@Override
public void eat(){
System.out.println("狗吃骨头");
}
}
- 静态方法不存在方法的重写
- 成员变量不存在子类成员变量覆写父类成员变量这一说法,只存在子类非静态成员方法覆写父类非静态成员方法这一说法
四、super关键字
1、作用:在Java类中使用super来调用父类中的指定操作:
(1)访问父类(直接父类)中定义的属性(非静态的成员变量)
(2)调用父类中定义的成员方法(非静态的)
(3)在子类构造器中调用父类的构造器
2、举例:以在父类中调用非静态的成员方法为例,非静态的成员变量类似
(1)this.eat()与eat()作用相同,在子类中的成员方法中使用eat()调用eat()时,子类中有则会调用子类中的eat(),子类中没有则会在直接父类中进行寻找(默认直接父类中是有的),调用父类中的eat()
子类中有eat()方法,调用子类中的eat()方法
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
@Override
public void eat(){
System.out.println("狗吃骨头");
}
}
package com.ffyc.bcms.dao;
public class XTQ extends Dog{
@Override
public void eat() {
System.out.println("哮天犬吃骨头");
}
public void fly(){
eat();// 调用eat()方法
System.out.println("哮天犬会飞");
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
XTQ xtq=new XTQ();
xtq.fly();
}
}
子类中没有eat()方法,调用父类中的eat()方法
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
@Override
public void eat(){
System.out.println("狗吃骨头");
}
}
package com.ffyc.bcms.dao;
public class XTQ extends Dog{
public void fly(){
eat();// 调用eat()方法
System.out.println("哮天犬会飞");
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
XTQ xtq=new XTQ();
xtq.fly();
}
}
(2)super.eat()调用的是即便子类中有eat()方法,调用的依然是父类中的eat()方法
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
@Override
public void eat(){
System.out.println("狗吃骨头");
}
}
package com.ffyc.bcms.dao;
public class XTQ extends Dog{
@Override
public void eat() {
System.out.println("哮天犬吃骨头");
}
public void fly(){
super.eat();// 调用eat()方法
System.out.println("哮天犬会飞");
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
XTQ xtq=new XTQ();
xtq.fly();
}
}
3、在子类XTQ的成员方法(非静态的)中调用直接父类中静态的成员方法stand()时,stand()、this.stand()、super.stand()三者的作用是等效的,调用的都是直接父类中的静态成员方法stand(),这里用super.stand()进行举例
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
public static void stand(){
System.out.println("狗站着");
}
@Override
public void eat(){
System.out.println("狗吃骨头");
}
}
package com.ffyc.bcms.dao;
public class XTQ extends Dog{
@Override
public void eat() {
System.out.println("哮天犬吃骨头");
}
public void fly(){
super.stand();
System.out.println("哮天犬会飞");
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
XTQ xtq=new XTQ();
xtq.fly();
}
}
4、注意:
(1)当子父类出现同名成员时,可用super表明调用的是父类中的成员
(2)super的追溯不仅限于直接父类还可以是父类中的父类,当然是一级一级地向上进行追溯
package com.ffyc.bcms.dao;
public class Animal {
public void eat(){
System.out.println("动物吃东西");
}
}
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
public static void stand(){
System.out.println("狗站着");
}
}
package com.ffyc.bcms.dao;
public class XTQ extends Dog{
@Override
public void eat() {
System.out.println("哮天犬吃骨头");
}
public void fly(){
super.eat();
System.out.println("哮天犬会飞");
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
XTQ xtq=new XTQ();
xtq.fly();
}
}
(3)super和this的用法相似,this代表本类对象的引用,super代表父类内存空间的标识
(4)super不是父类对象,在创建子类对象时,会加载其所有父类,但不会创建父类对象,只会将父类中的信息加载到子类的对象中存储
5、使用super在子类构造器中调用父类构造器(子类与直接父类)
(1)子类继承父类时不会继承父类的构造方法,只能通过super(参数列表)的方式调用父类指定的构造方法
package com.ffyc.bcms.dao;
public class Animal {
private String name;
private int age;
public Animal(){
System.out.println("Animal类中的无参构造器");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Animal类中的有参构造器");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public Dog() {
super();// 调用父类中的无参构造器
System.out.println("Dog类的无参构造器");
}
public Dog(String name, int age) {
super(name, age);// 调用父类中的有参构造器
System.out.println("Dog类的有参构造器");
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
Dog dog=new Dog("旺财",13);
System.out.println(dog.getName());
System.out.println(dog.getAge());
Dog dog1=new Dog();
dog1.setName("大宝");
dog1.setAge(14);
System.out.println(dog1.getName());
System.out.println(dog1.getAge());
}
}
(2)在Java的语法中,super(形参列表)如果有的话,必须在子类构造器的首行
(3)如果在子类构造器的首行没有显示调用super(形参列表),则子类此构造器默认调用super()这个父类中无参的构造器
public class Dog extends Animal{
public Dog() {
System.out.println("Dog类的无参构造器");
}
public Dog(String name, int age) {
System.out.println("Dog类的有参构造器");
}
}
在子类的构造方法中使用super调用父类的构造方法的目的:保证先对父类成员进行初始化
注意:如果子类构造器中既未显示调用父类或本类的构造器,且父类中又没有无参的构造器,则程序在编译时会报错
情况一:父类中没有无参构造器,且子类构造器中未显示调用父类或本类的构造器
情况二:父类中没有无参构造器,但子类构造器中显示调用了父类或本类的构造器
package com.ffyc.bcms.dao;
public class Animal {
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Animal类中的有参构造器");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.ffyc.bcms.dao;
public class Dog extends Animal{
public Dog() {
this("小宝",11);// 在父类没有无参构造方法的情况下调用本类中的构造方法
System.out.println("Dog类的无参构造器");
}
public Dog(String name, int age) {
super(name,age);// 在父类没有无参构造方法的情况下调用父类中的构造方法
System.out.println("Dog类的有参构造器");
}
}
package com.ffyc.bcms.dao;
public class Test {
public static void main(String[] args) {
Dog dog=new Dog("旺财",13);
System.out.println(dog.getName());
System.out.println(dog.getAge());
Dog dog1=new Dog();
System.out.println(dog1.getName());
System.out.println(dog1.getAge());
}
}
情况三:this显示调用子类中的构造器时也必须放在第一行,否则程序在编译时会出错