super关键字的使用
1.super理解为:父类的
2.super可以用来调用:属性,方法,构造器
3.super的使用:调用属性和方法
3.1 我们可以在子类的方法或构造器中,通过使用“super.属性”或“super.方法”的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略“super.”
3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用super.属性的方式,表明调用的的是父类中声明的属性
3.3 特殊情况:当子类重写了父类中的方法时,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用super.方法的方式,表明调用的的是父类中声明的属性
public class Person{
String name;
int age;
int id=1001;//身份证号
public Person(){
}
public Person(String name){
this.name=name;
}
public Person(String name,int age){
this(name);
this.age=age;
}
public void eat(){
System.out.println("人:吃饭");
}
public void walk(){
System.out.println("人:走路");
}
}
public class Student extends Person{
String major;
int id=1002;//学号
public Student(){
}
public Student(String major){
this.major=major;
}
public void eat(){
System.out.println("学生:多吃有营养的食物");
}
public void study(){
System.out.println("学生:学习知识");
}
public void show(){
System.out.println("name="+this.name+",age="+super.age);
System.out.println("id="+this.id);
System.out.println("id="+super.id);
}
}
public class SuperTest{
public static void main(String[] args){
Student s=new Student();
s.show();
}
4.super调用构造器
4.1我们可以在子类的构造器中显式的使用super(形参列表)的方式,调用父类中声明的构造器
4.2super(形参列表)的使用,必须声明在子类构造器的首行
4.3我们在类的构造器中,针对于this(形参列表)或super(形参列表)只能二选一,不能同时出现
4.4在构造器的首行,没有显式的声明this(形参列表)或super(形参列表),则默认调用的是父类中空参的构造器:super()
4.5 在类的多个构造器中,至少有一个类的构造器中使用了super(形参列表),调用了父类的构造器
public class Student extends Person{
String major;
int id=1002;//学号
public Student(){
}
public Student(String major){
this.major=major;
}
public Student(String name,int age,String major){
//this.name=name;
//this.age=age;
super(name,age);
this.major=major;
public void eat(){
System.out.println("学生:多吃有营养的食物");
}
public void study(){
System.out.println("学生:学习知识");
}
public void show(){
System.out.println("name="+this.name+",age="+super.age);
System.out.println("id="+this.id);
System.out.println("id="+super.id);
}
}
public class SuperTest{
public static void main(String[] args){
Student s=new Student();
s.show();
Student s1=new Student("Tom",21,"IT");
s1.show();
}
}
1.从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性
2.从过程上来讲
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象
public class InstanceTest{
}
写一个名为Account的类模拟账户。该类的属性和方法如下图所示。
* 该类包括的属性:账号id,余额balance,年利率annualInterestRate;
* 包含的方法:访问器方法(getter和setter方法),
* 返回月利率的方法getMonthlyInterest(),
* 取款方法withdraw(),存款方法deposit()。
public class Account{
private int id;
private double balance;
private double annualInterestRate;
public Account(int id,double balance,double annualInterestRate){
this.id=id;
this.balance=balance;
this.annualInterestRate=annualInterestRate;
}
public int getId(){
return id;
}
public void setId(int id){
this.id=id;
}
public double getBalance(){
return balance;
}
public void setBalance(double balance){
this.balance=balance;
}
public double getAnnualInterestRate(){
return annualInterestRate;
}
public void setAnnualInterestRate(double annualInterestRate){
this.annualInterestRate=annualInterestRate;
}
//返回月利率
public double getMonthlyInterest(){
return annualInterestRate/12;
}
//取钱
public void withdraw(double amount){
if(balance>=amount){
balance-=amount;
return;
}
System.out.println("余额不足");
}
//存钱
public void deposit(double amount){
if(amount>0){
balance+=amount;
}
}
}
写一个用户程序测试Account类。在用户程序中,
* 创建一个账号为1122、余额为20000、年利率4.5%的Account对象。
* 使用withdraw方法提款30000元,并打印余额。再使用withdraw方法提款2500元,
* 使用deposit方法存款3000元,然后打印余额和月利率。
public class AccountTest{
public static void main(String[] args){
Account acct=new Account(1122,20000,0.045);
acct.withdraw(30000);
System.out.println("您的账户余额为:"+acct.getBalance());
acct.withdraw(2500);
System.out.println("您的账户余额为:"+acct.getBalance());
acct.deposit(3000);
System.out.println("您的账户余额为:"+acct.getBalance());
System.out.println("月利率为:"+(acct.getMonthlyInterest()*100)+"%");
}
}
* 创建Account类的一个子类CheckAccount代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额。
* 在CheckAccount类中重写withdraw方法,其算法如下:
* 如果(取款金额<账户余额),
* 可直接取款
* 如果(取款金额>账户余额),
* 计算需要透支的额度
* 判断可透支额overdraft是否足够支付本次透支需要,如果可以
* 将账户余额修改为0,冲减可透支金额
* 如果不可以
* 提示用户超过可透支额的限额
public class CheckAccount extends Account{
private double overdraft;//可透支限额
public CheckAccount(int id,double balance,double annualInterestRate,double overdraft){
super(id,balance,annualInterestRate);
this.overdraft=overdraft;
}
public double getOverdraft(){
return overdraft;
}
public void setOverdraft(double overdraft){
this.overdraft=overdraft;
}
public void withdraw(double amount){
if(getBalance()>=amount){//余额足够消费
//getBalance()-=amount;
setBalance(getBalance()-amount);
//或super.withdraw(amount);
}else if(overdraft>=amount-getBalance()){//透支额度加上余额足够消费
overdraft-=(amount-getBalance());
setBalance(0);
}else{
System.out.println("超过可透支限额!");
}
}
}
写一个用户程序测试CheckAccount类。在用户程序中,
* 创建一个账号为1122、余额为20000、年利率4.5%,
* 可透支限额为5000元的CheckAccount对象。
* 使用withdraw方法提款5000元,并打印账户余额和可透支额。
* 再使用withdraw方法提款18000元,并打印账户余额和可透支额。
* 再使用withdraw方法提款3000元,并打印账户余额和可透支额。
public class CheckAccountTest{
public static void main(String[] args){
CheckAccount acct=new CheckAccount(1122,20000,0.045,5000);
acct.withdraw(5000);
System.out.println("您的账户余额为:"+acct.getBalance());
System.out.println("您的可透支余额为:"+acct.getOverdraft());
acct.withdraw(18000);
System.out.println("您的账户余额为:"+acct.getBalance());
System.out.println("您的可透支余额为:"+acct.getOverdraft());
acct.withdraw(3000);
System.out.println("您的账户余额为:"+acct.getBalance());
System.out.println("您的可透支余额为:"+acct.getOverdraft());
}
}
public class Person{
String name;
int age;
public void eat(){
System.out.println("人:吃饭");
}
public void walk(){
System.out.println("人:走路");
}
}
public class Man extends Person{
boolean isSmoking;
public void earnMoney(){
System.out.println("男人负责挣钱养家");
}
public void eat(){
System.out.println("男人多吃肉,长肌肉");
}
public void walk(){
System.out.println("男人霸气的走路");
}
}
public class Woman extends Person{
boolean isBeauty;
public void goShopping(){
System.out.println("女人喜欢购物");
}
public void eat(){
System.out.println("女人少吃,为了减肥");
}
public void walk(){
System.out.println("女人走路");
}
}
public class PersonTest{
public static void main(String[] args){
Person p1=new Person();
p1.eat();
Man man=new Man();
man.eat();
man.age=25;
man.earnMoney();
//**************************************
//对象的多态性:父类的引用指向子类的对象
Person p2=new Man();
Person p3=new Woman();
//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法--虚拟方法调用
p2.eat();
p2.walk();
// p2.earnMoney();
}
}
1.理解多态性,可以理解为一个事物的多种形态
2.何为多态性
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
3.多态的使用:虚拟方法调用
有了对象的多态性之后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法
总结:编译,看左边;运行,看右边
4.多态性使用前提:
4.1 要有类的继承关系
4.2方法的重写
public class AnimalTest{
public static void main(String[] args){
AnimalTest test=new AnimalTest();
test.func(new Dog());
test.func(new Cat());
}
public void func(Animal animal){//Animal animal=new Dog();
animal.eat();
animal.shout()
}
//public void func(Dog dog){
// dog.eat();
// dog.shout();
//}
}
class Animal{
public void eat(){
System.out.println("动物:进食");
}
public void shout(){
System.out.println("动物:叫");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
public void shout(){
System.out.println("汪汪汪");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void shout(){
System.out.println("喵喵喵");
}
}
//举例
class Driver{
public void doData(Connection conn){//conn=new MySQLConnection/conn=newOracleConnection
//规范的步骤去操作数据
//conn.method1();
}
}
5.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
2.从编译和运行的角度看:
* 重载,是指允许存在多个同名方法,而这些方法的参数不同。
* 编译器根据方法不同的参数表,对同名方法的名称做修饰。
* 对于编译器而言,这些同名方法就成了不同的方法。
* 它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,
* 即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,
* 编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
* 而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,
* 这称为“晚绑定”或“动态绑定”。
*
* 引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
*/
//面试题:多态是编译时行为还是运行时行为?
//证明如下:
class Animal {
protected void eat() {
System.out.println("animal eat food");
}
}
class Cat extends Animal {
protected void eat() {
System.out.println("cat eat fish");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("Dog eat bone");
}
}
class Sheep extends Animal {
public void eat() {
System.out.println("Sheep eat grass");
}
}
public class InterviewTest {
public static Animal getInstance(int key) {
switch (key) {
case 0:
return new Cat ();
case 1:
return new Dog ();
default:
return new Sheep ();
}
}
public static void main(String[] args) {
int key = new Random().nextInt(3);
System.out.println(key);
Animal animal = getInstance(key);
animal.eat();
}
}
public class Person {
String name;
int age;
public void eat(){
System.out.println("人,吃饭");
}
public void walk(){
System.out.println("人,走路");
}
}
public class Man extends Person{
boolean isSmoking;
public void earnMoney(){
System.out.println("男人负责工作养家");
}
public void eat() {
System.out.println("男人多吃肉,长肌肉");
}
public void walk() {
System.out.println("男人霸气的走路");
}
}
public class Woman extends Person{
boolean isBeauty;
public void goShopping(){
System.out.println("女人喜欢购物");
}
public void eat(){
System.out.println("女人少吃,为了减肥。");
}
public void walk(){
System.out.println("女人,窈窕的走路。");
}
}
public class PersonTest{
public static void main(String[] args){
Person p1=new Person();
p1.eat();
Man man=new Man();
man.eat();
man.age=25;
man.earnMoney();
//**************************************
//对象的多态性:父类的引用指向子类的对象
Person p2=new Man();
Person p3=new Woman();
//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法--虚拟方法调用
p2.eat();
p2.walk();
// p2.earnMoney();
System.out.println(p2.id);//1001
//不能调用子类所特有的方法,属性:编译时,p2是Person类型
p2.name="Tom";
//p2.isSmoking=true;
//有了对象的多态性之后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性或方法,子类特有的属性和方法不能调用
//如果才能调用子类特有的属性或方法
//向下转型:使用强制类型转换符
Man m1=(Man)p2;
m1.earnMoney();
m1.isSmoking=true;
//使用强转时,可能出现ClassCastExpection的异常
//Woman w1=(Woman)p2;
//w1.goShopping();
}
a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false
实用情景:为了避免在向下转型时出现ClassCastExpection的异常,我们在向下转型之前,先进行intstanceof的判断,一旦返回true,就进行向下转型,如果返回false,不进行向下转型
如果a instanceof A返回true,则a instanceof B也返回true
其中,类B是类A的父类
if(p2 instanceof Woman){
Woman w1=(Woman)p2;
w1.goShopping();
System.out.println("******Woman*********");
}
if(p2 instanceof Man){
Man m2=(Man)p2;
m2.earnMoney();
System.out.println("*****Man************");
}
if(p2 instanceof Person){
System.out.println("*****Person************");
}
if(p2 instanceof Object){
System.out.println("*****Object************");
}
练习
问题一:编译时通过,运行时不通过
//举例一
//Person p3=new Woman();
//Man m3=(Man) p3;
//举例二
Person p4=new Person();
Man m4=(Man)p4;
问题二:编译通过,运行时也通过
Object obj=new Woman();
Person p=(Person)obj;
//问题三:编译不通过 不相关的两个类不能运行
Man m5=new Woman();
String str=new Date();
Object o=new Date();
String str1=(String)o;
public class FieldMethodTest {
public static void main(String[] args){
Sub s= new Sub();
System.out.println(s.count); //20
s.display();//20
Base b = s;
//==:对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否一样。
System.out.println(b == s); //true
System.out.println(b.count); //10
b.display();
}
}
class Base {
int count= 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count= 20;
public void display() {
System.out.println(this.count);
}
}
练习:
1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,
* 系统将不可能把父类里的方法转移到子类中。编译看左边,运行看右边
*
* 2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,
* 这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看右边
建立InstanceTest 类,在类中定义方法method(Person e);
*
* 在method中:
* (1)根据e的类型调用相应类的getInfo()方法。
* (2)根据e的类型执行:
* 如果e为Person类的对象,输出:“a person”;
* 如果e为Student类的对象,输出:“a student”“a person ”
* 如果e为Graduate类的对象,输出:“a graduated student”
* “a student” “a person”
public class InstanceTest{
public static void main(String[] args){
Instance test=new InstanceTest();
test.method(new Student());
}
public void method(Person e){
//虚拟方法调用
String info=e.getInfo();
System.out.println(info);
if(e instanceof Graduate){
System.out.println("a graduated student");
System.out.println("a student");
System.out.println("a person");
}else if(e instanceof Student){
System.out.println("a student");
System.out.println("a person");
}else{
System.out.println("a person");
}
}
}
class Person {
protected String name = "person";
protected int age = 50;
public String getInfo() {
return "Name: " + name + "\n" + "age: " + age;
}
}
class Student extends Person {
protected String school = "pku";
public String getInfo() {
return "Name: " + name + "\nage: " + age + "\nschool: " + school;
}
}
class Graduate extends Student {
public String major = "IT";
public String getInfo() {
return "Name: " + name + "\nage: " + age + "\nschool: " + school + "\nmajor:" + major;
}
}