一. 继承性的格式:class A extend B{} A:子类,派生类,subclass B:父类,超类,基类,superclass **二. 继承性的体现:**一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有属性和方法。特别地是父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只因为封装性的影响,使得子类不能直接调用父类的结构而已。 子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的扩展。 三. Java中关于继承性的规定: 1.一个类中可以被多个子类继承 2.Java中类的单继承性:一个类只能有一个父类 3.子类是相对的概念 4.子类直接继承的父类称为:直接父类。间接继承的父类称为:间接父类 5.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法 四.补充: 1.如果我们没有显式的声明一个类的父类的话,则此类继承与java.lang.Object类 2.所有的java类(除java.lang.Object类之外)都直接或间接地继承与java.lang.Object 3.所有的java类具有java.lang.Object类声明的功能 练习:
public class Circle { private double radius; public Circle() { radius=1.0; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius=radius; } public double findArea() { return Math.PI*radius*radius; } } public class Cylinder extends Circle{ double length; public Cylinder() { length=1.0; } public double getLength() { return length; } public void setLength(double length) { this.length = length; } public double findVolume() { return findArea()*length; } }重写:子类继承父类之后,可以对子类中同名同参数的方法,进行覆盖操作 应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名参数的方法时,实际执行的是子类重写父类的方法 重写的规定: 方法的声明:权限修饰符 返回值类型 方法名(形参列表){//方法体} 1.子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同 2.子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符。 3.父类被重写的方法的返回值类型是void,则子类被重写的方法的返回值类型真实void 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以使A类或A类的子类 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型 4.子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型 5.子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的。
public class Cylinder extends Circle{ double length; public Cylinder() { length=1.0; } public double getLength() { return length; } public void setLength(double length) { this.length = length; } public double findVolume() { return findArea()*length; } @Override public double findArea() {//表面积 // TODO Auto-generated method stub return 2*Math.PI*getRadius()*getRadius()+2*Math.PI*getRadius()*length; } }super可以用来调用属性,方法,构造器 super的使用: 1.在子类的方法或构造器中,通过使用“super.属性”或“super.方法”的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略super 2.特殊情况,当父类和子类中定义了同名的属性时,在子类中调用父类中的声明的属性,则必须显式的使用“super.属性”的方式,表明调用的是父类中声明的属性。 3.特殊情况,当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式使用“super.方法”的方式,表明调用的是父类中被重写的方法。 super调用构造器 1.可以在子类的构造器中显式的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器 2.“super(形参列表)”的使用,必须声明在子类构造器的首行 3.在类的构造器中,针对于“this(形参列表)”或“super(形参列表)”只能二选一 4.在构造器的首行,没有显式的声明“this(形参列表)”或“super(形参列表)”,则默认调用的是父类中空参的构造器 所以如果去掉父类空参构造器并且子类没有显示声明“this(形参列表)”或“super(形参列表)”,子类报错。解决办法:父类加上空参构造器或者调用指定构造器。 5.再累的多个构造器中,至少有一个构造器使用了“super(形参列表)”,调用父类中的构造器。
public class Circle { private double radius; public Circle() { radius=1.0; } public Circle(double radius) { this.radius=radius; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius=radius; } public double findArea() { return Math.PI*radius*radius; } } public class Cylinder extends Circle{ double length; public Cylinder() { //super();默认有super length=1.0; } public Cylinder(double radius,double length) { super(radius); this.length=length; } public double getLength() { return length; } public void setLength(double length) { this.length = length; } public double findVolume() { return findArea()*length; } @Override public double findArea() {//表面积 // TODO Auto-generated method stub return 2*super.findArea()+2*Math.PI*getRadius()*length; } }思考: 1).为什么super(…)和this(…)调用语句不能同时在一个构造器中出现? 2).为什么super(…)或this(…)调用语句只能作为构造器中的第一句出现?
public class Person{ public Person(){ } } public class student extends Person{ int age; public student(){ //super(); } public student(int age){ super(); this(); this.age=age; } }在这个例子中,当调用子类有参构造器时,先调用了父类的空参构造器,然后运行this(),调用自己的空参构造器,子类空参构造器又调用了一次父类的空参构造器,父类初始化了两次。
1.从结果上来看:(继承性) 子类继承父类以后,就获取了父类中声明的属性或方法。 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。 2.从过程上来看:当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用间接父类的构造器,直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才能看到内存中有父类中的结构,子类对象才可以考虑进行调用。 明确:虽然创建子类对象时,调用了父类的构造器但是自始至终就创建过一个对象,即为new的子类对象。 练习
public class Account { private int id; private double balance; private double annualInterestRate; public Account(int id,double balance,double annualInterestRate ) { this.annualInterestRate= annualInterestRate; this.balance=balance; this.id=id; } 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 void withdraw(double amount) { if(balance>=amount) { balance-=amount; }else { System.out.println("余额不足!"); } } public void deposit(double amount) { balance+=amount; } } 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; } @Override public void withdraw(double amount) { if(getBalance()>=amount) {setBalance(getBalance()-amount);}//方式一,一般来说balance不能set //super.withdraw(amount);方式二 //方式三:该权限 else if(overdraft>=amount-getBalance()) { overdraft-=(amount-getBalance()); setBalance(0);//注意顺序 }else {System.out.println("超出可透支限额");} } } public class classTest1 { public static void main(String[] args) { CheckAccount a=new CheckAccount(1122,20000,0.045,5000); a.withdraw(5000); System.out.println("您的账户余额为:"+a.getBalance()); System.out.println("您的可透支额为:"+a.getOverdraft()); System.out.println(); a.withdraw(18000); System.out.println("您的账户余额为:"+a.getBalance()); System.out.println("您的可透支额为:"+a.getOverdraft()); System.out.println(); a.withdraw(3000); System.out.println("您的账户余额为:"+a.getBalance()); System.out.println("您的可透支额为:"+a.getOverdraft()); } }1.理解多态性,可以理解为一个事物的多种形态 2.对象的多态性:父类的引用指向子类对象 Person p1=new Man(); 3.多态的使用:虚拟方法调用 有了对象的多态性后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。 总结:编译:看左边;运行:看右边 4.多态性的使用前提:类的继承关系,方法的重写 5.对象的多态性,只适用于方法,不适用于属性 6.多态性是运行时行为 7.有了对象的多态性以后,内存中世纪上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。 多态性的理解 1.实现代码的通用性 2.object类中定义的public boolean equals(Object obj){} 3.抽象类,接口的使用肯定体现了多态性。(抽象类,接口不能实例化)
public class classTest1 { public static void main(String[] args) { classTest1 test=new classTest1(); Circle c1=new Cylinder(); test.func(new Cylinder()); } public void func(Circle c) {//Circle c=new Cylinder(); c.findArea(); } }面试题:方法的重载与重写 1.定义 方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。 方法的重写子类继承父类之后,可以对子类中同名同参数的方法,进行覆盖操作 2.具体规则:权限修饰符 返回值类型 方法名(形参列表) 重写:方法名一样,形参列表不一样,与权限修饰符,返回值类型 无关 重载:方法名,形参列表一样 子类权限修饰符大于等于父类 返回值类型:void–void;类–类及其子类;基本数据类型–相同的基本数据类型 3.从编译运行的角度看:重载不表现为多态性,重写表现为多态性 重载,是指允许存在多个同名方法,而这些方法的参数不同。 编译器根据方法不同的参数表, 对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。 它们的调用地址在编译期就绑定了。 Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。 所以: 对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或**“静态绑定”** ; 而对于多态,只有等到方法调用的那一刻, 解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定” 。 调用子类特有的属性和方法
//向下转型:使用强制类型转换符 Person p1=new Man(); Man m1=(Man)p1;//地址赋给m1,同时把地址值类型转为Man m1.isSmoking=true;//isSmoking为m1特有的 //使用强转时,出现ClassCastException异常 Woman w1=(Woman)p1; w1.goShopping(); //修改后 if(p2 instanceof Woman){ Woman w1=(Woman)p1; w1.goShopping(); }内存解析 instanceof操作符 x instanceof A:检验x是否为类A的对象,如果是返回true,不是 false。 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。如果x属于类A的子类B, x instanceof A值也为true 使用情景:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不行行向下转型。
public class classTest1 { public static void main(String[] args) { Person p1=new Man(); if(p1 instanceof Man) { Man m1=(Man)p1; System.out.println("man");//man } if(p1 instanceof Person) { System.out.println("person");//person } if(p1 instanceof Object) { System.out.println("object");//object } } } class Person{ } class Man extends Person{ } class Woman extends Person{ }向下转型的常见情况
//1.编译时通过,运行时不通过 Person p2=new Woman(); Man m2=(man)p2; / Person p3=new Person(); Man m3=(man)p3; // Object o=new Date(); String str1=(String)o; //2.编译时通过,运行时通过 Object obj=new Woman(); Person p=(person)obj;//woman---object----person //obj可以转为woman,自然可以转为person //3.编译时不通过 Man m4=new Woman(); String str=new Date();练习1
public class classTest1 { 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();//20,子类重写的方法 } } 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.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
练习2
public class classTest1 { public static void main(String[] args) { classTest1 test=new classTest1(); Person p1=new Student(); test.method(p1); } public void method(Person e) { e.getInfo(); if(e instanceof Graduate) {//条件范围从小到大 System.out.println("a graduated student"); } if(e instanceof Student) { System.out.println("a student"); } if(e instanceof Person) { System.out.println("a Person"); } /* if(e instanceof Graduate) {//方式二 System.out.println("a graduated student"); System.out.println("a student"); System.out.println("a Person"); } * */ } }练习3
public class classTest1 { public static void main(String[] args) { Base1 base = new Sub1(); base.add(1, 2, 3);//sub_1,调用子类重写的方法 Sub1 s = (Sub1)base; s.add(1,2,3);//sub_2,调用子类方法 } } class Base1 { public void add(int a, int... arr) { System.out.println("base1"); } } class Sub1 extends Base1 { public void add(int a, int[] arr) { System.out.println("sub_1"); } public void add(int a, int b, int c) { System.out.println("sub_2"); } }面试题:多态是编译时行为还是运行时行为?如何证明? 运行时行为
import java.util.Random; 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(); } } 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"); } }采用random随机数随机生成对象,编译时并不知道会产生何种对象,只有在运行时才能判断。
java.lang.Object类 1.Object类是所有Java类的根父类 2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类 3.Object类中的功能(属性,方法)具有通用性 属性:无 方法:equals()/toString()/getClass()/hashCode()/clone()/finalize()/wait()/notify()/notifyAll() 4.Object类只声明了一个空参的构造器 (数组也作为object类的子类出现,可以调用object类中声明的方法)
运算符==的使用 1.可以使用在基本数据类型变量和引用数据类型变量中 2.如果比较的是基本数据类型变量:比较保存的数据是否相等(不一定类型要相同,类型提升) 引用数据类型:比较对象的地址值,即两个引用是否指向同一个对象实体 equals()方法的使用 1.方法,而不是运算符 2.只适用于引用数据类型 3.Object类中equals()的定义: public boolean equals(Object obj){ return (thisobj); } 说明:Object类中equals()和作用相同,比较两个对象的地址值是否相同 4.像String,Date,File,包装类等都重写了Object类中equals()方法。重写以后,比较的是两个对象的实体内容是否相同。 重写equals() 通常情况下,我们自定义类如果使用equals()的话,也通常是比较两个对象的“实体内容”是否相同。那么,就需要对Object类中equals()进行重写。 重写的原则:比较两个对象的实体内容相同
class Person{ int age; String name; @Override public boolean equals(Object obj) { if(this==obj) { return true; } if(obj instanceof Person) { Person p=(Person)obj; //if (this.age==p.age&& this.name.equals(p.name)) {//方式一 // return true; return this.age==p.age&& this.name.equals(p.name)//方式二 //}else {return false;} } return false; } }自动生成equals()
class Person{ int age; String name; @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }手写的equals和自动生成的区别
public void test(){ Person p= new Person("Tom",12); Man m=new Man("Tom",12); System.out.println(p.equals(m)); //手动生成的equals,true;obj instanceof Person 这个判断为true //自动生成的equals,false;getclass更加严谨 }练习1 练习2
public class classTest1 { public static void main(String[] args) { Order o1=new Order(1001,"AA"); Order o2=new Order(1001,"BB"); System.out.println(o1.equals(o2));//false Order o3=new Order(1001,"BB"); System.out.println(o2.equals(o3));//true,equals方法中判断name相等改为==,也会是true Order o4=new Order(1001,new String("BB")); System.out.println(o3.equals(o4));//true,equals方法中判断name相等改为==,会是false,新建了一个“bb” String s1="BB"; String s2="BB"; System.out.println(s1==s2);//true,这是因为String放在常量池中,指向同一个bb } } class Order{ int orderId; String name; public Order(int orderId, String name) { this.orderId = orderId; this.name = name; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Order other = (Order) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (orderId != other.orderId) return false; return true; } }内存解析: Object类中toString()的使用: 1.当我们输出一个对象的引用时,实际上就是调用当前对象的toString() 2.定义 public String toString(){ retrun getClass().getName()+"@"+Integer.toHexString(hashCode()); } 3.像String,Date,File,包装类等都重写了Object类中的toString()方法,使得在调用对象的toString()时,返回实体内容信息。 4.自定义类也可以重写toString()方法,当调用此方法时,返回对象的实体内容。
public class classTest1 { public static void main(String[] args) { Order o1=new Order(1001,"AA"); //System.out.println(o1.toString());//com.atguigu.class2.Order@15db9742 //System.out.println(o1);//com.atguigu.class2.Order@15db9742 System.out.println(o1.toString());//Order [orderId=1001, name=AA] System.out.println(o1);//Order [orderId=1001, name=AA] String str=new String("AA");//AA System.out.println(str); Date date=new Date(45345534543L); System.out.println(date.toString());//1971-06-10 } } class Order{ int orderId; String name; public Order(int orderId, String name) { this.orderId = orderId; this.name = name; } @Override public String toString() {//可自定义或自动生成 return "Order [orderId=" + orderId + ", name=" + name + "]"; } }练习:
char[] arr = new char[] { 'a', 'b', 'c' }; System.out.println(arr);//abc 因为char的println功能就是输出内容 System.out.println(arr.toString());//[C@15db9742 int[] arr1 = new int[] { 1, 2, 3 }; System.out.println(arr1);//[I@15db9742 double[] arr2 = new double[] { 1.1, 2.2, 3.3 }; System.out.println(arr2);//[D@6d06d69c System.out.println(arr2.toString());//[D@6d06d69c public static void main(String[] args) { String s="abc"; System.out.println(s);//abc,如果不是null输出内容,是null返回null System.out.println(s.toString());//abc,只是return this ,若是null肯定报错 s=null; System.out.println(s);//null System.out.println(s.toString());//出现NullPointerException }Java中的JUnit单元测试 步骤: 1.选中当前工程-右键选择:buildpath-add libraries-JUit 4 2.创建java类:此类是public,此类提供公共的无参构造器 3.此类中声明单元测试的方法:此时的单元测试方法权限是public,没有返回值,没有形参 4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test; 5.声明好单元测试方法以后,就可以在方法体内测试相关的代码 6.写完代码之后,左键双击单元测试方法名,右键:run as-JUit Test 说明:如果执行结果没有出现任何异常:绿条 出现异常:红
包装类的使用 1.java提供了8中基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征。 2.掌握:基本数据类型,包装类,String三者之间的相互转换
基本数据类型–>包装类
import org.junit.Test; public class WrapperTest { //基本数据类型-->包装类:调用包装类的构造器 @Test public void test1() { int num=10; //System.out.println(num.toString());报错 Integer in1=new Integer(num); System.out.println(in1.toString());//10 Integer in2=new Integer("123"); System.out.println(in2.toString());//123 //报异常 //Integer in3=new Integer("123abc"); //System.out.println(in3.toString()); Float f1=new Float(12.3f); Float f2=new Float("12.3"); System.out.println(f1); System.out.println(f2); Boolean b1=new Boolean(true); Boolean b2=new Boolean("true"); Boolean b3=new Boolean("true123"); System.out.println(b3);//false //不区分大小写,只要不是null和true,一律认为是false Order order=new Order(); System.out.println(order.isMale);//false System.out.println(order.isFemale);//null } } class Order{ boolean isMale; Boolean isFemale; }包装类–>基本数据类型
public class WrapperTest { //包装类-->基本数据类型:调用包装类Xxx的xxxvalue() @Test public void test2(){ Integer in1=new Integer(12); int i1=in1.intValue(); System.out.println(i1+1);//13 Float f1=new Float(12.3f); float f2=f1.floatValue(); System.out.println(f2+1);//13.3 } }自动装箱与自动拆箱
//JDK 5.0 新特性:自动装箱与自动拆箱 @Test public void test3() { //基本数据类型-->包装类的对象 //自动装箱 int num=10; Integer in1=num; boolean b1=true; Boolean b2=b1; //包装类-->基本数据类型 //自动拆箱 System.out.println(in1.toString()); int num1=in1; }基本数据类型,包装类–>String类型
//基本数据类型,包装类-->String类型:调用String重载的valueOf(Xxx xxx) @Test public void test4() { int num1=10; // String str1=num1+""; // float f1=12.3f; String str2=String.valueOf(f1);//"12.3" Double d1=new Double(12.4); String str3=String.valueOf(d1); System.out.println(str2); System.out.println(str3); Integer in2=12; String str4=in2.toString(); System.out.println(str4);//12 String str5=in2.toString(111); System.out.println(str5);//111 }String类型–>基本数据类型,包装类
//String类型-->基本数据类型,包装类:调用包装类的parseXxx(String s),包装类的构造器也可以实现见前面代码 @Test public void test5() { String str1="123"; //错误的情况 // int num=(int)str1; // Integer in1=(Integer)str1; int num=Integer.parseInt(str1); System.out.println(num+1);//124 String str2="true1"; boolean b1=Boolean.parseBoolean(str2); System.out.println(b1);//false }练习1
public class InterviewTest { @Test public void test1() { Object o1 = true ? new Integer(1) : new Double(2.0); System.out.println(o1);// 1.0 类型提升,编译时前后类型要统一 } @Test public void test2() { Object o2; if (true) o2 = new Integer(1); else o2 = new Double(2.0); System.out.println(o2);// 1 } @Test public void test3() { Integer i = new Integer(1); Integer j = new Integer(1); System.out.println(i == j);//false //Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[], //保存了从-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在 //-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的:提高效率 Integer m = 1; Integer n = 1; System.out.println(m == n);//true Integer x = 128;//相当于new了一个Integer对象 Integer y = 128;//相当于new了一个Integer对象 System.out.println(x == y);//false } }练习2
public class classTest1 { public static void main(String[] args) { Vector v=new Vector(); Scanner scan=new Scanner(System.in); String level; int maxscore=0; for(;;) { System.out.println("请输入学生成绩(负数表示结束)"); int score=scan.nextInt(); //Integer inscore=score; if(score<0) { break; } if(score>100) { System.out.println("输入的数据非法,重新输入"); continue; } if(score>maxscore) {//最大值 maxscore=score; } //v.addElement(inscore); v.addElement(score);//自动装箱 } for(int i=0;i<v.size();i++) { Object obj=v.elementAt(i); Integer in2=(Integer)obj; int num=in2; if(maxscore-num<=10) {level="A";} else if(maxscore-num<=20) {level="B";} else if(maxscore-num<=30) {level="C";} else {level="D";} System.out.println("student-"+i+" score is "+num+" level is "+level); } } }