目录
* 2、继承性的格式:class A extends B{}
一、面向对象特征之二:继承性
* 1、继承性的好处
* 1.减少了代码的冗余,提高了代码的复用性
* 2.便于功能的扩展
* 3.为之后多态性的使用,提供了前提
* 2、继承性的格式:class A extends B{}
* A:子类、派生类、subclass
* B:父类、超类、基类、superclass
*
* 2.1体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法
* 特别的,父类中声明为private的属性和方法,子类继承父类以后,仍然认为获取了父类中私有的结构
* 只是因为封装性的影响,使得子类不能直接调用父类的结构而已
* 2.2子类继承父类以后,还可以声明自己特有的属性和方法,实现功能的扩展
* 子类和父类的关系,不同于子集和集合的关系
* extends:延展、扩展
* 3、Java中关于继承性的规定
* 3.1.一个类可以被多个子类继承
* 3.2.Java中类的单继承性:一个类只能有一个父类
* 3.3.子父类是相对的概念
* 3.4.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
* 3.5.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
* 4、java.lang.Object类的理解
* 4.1.如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
* 4.2.所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
* 4.3.意味着,所有的java类具有java.lang.Object类声明的功能
二、方法的重写(override/overwrite)
* 1.重写:子类继承父类以后,可以对父类中 同名同参数 的方法,进行覆盖操作
* 2.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中同名同参数的方法时,实际执行的是子类重写父类的方法
* 3.重写的规定:
* 方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
* 方法体
* }
* 约定俗称:子类中的叫重写的方法。父类中的叫被重写的方法
* ①子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
* ②子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
* >特殊情况:子类不能重写父类中声明为private权限的方法
* ③返回值类型
* >父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型也只能是void
* >父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
* >父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
* ④子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理讲)
* *************************************************************************************
* 子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)
*
面试题:区分方法的重载与重写
答 :①二者的概念
②重载和重写的具体规则
③重载:不表现为多态性(对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法, 这称为“早绑定”或“静态绑定”)
重写:表现为多态性(对于多态,只等到方法调用的那一刻,解释运行器才会确定所要调用的具体 方法,这称为“晚绑定”或“动态绑定”)
三、子类对象实例化的全过程
* 1.从结果上来看:(继承性)
* 子类继承父类以后,就获取了父类中声明的属性或方法
* 创建子类的对象,在堆空间中,就会加载所父类中声明的属性
* 2.从过程上来看:
* 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用
* 父类的父类的构造器...直到调用了java.lang.Object类中的空参构造器为止。正因为加载过所的
* 父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用
明确:
虽然创建子类对象时调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象
四、super关键字的使用
* 1.super理解为:父类的
* 2.super可以用来调用:属性、方法、构造器
* 3.super的使用:调用属性和方法
* 3.1我们可以在子类的方法或构造器中,通过使用“super.属性”或“super.方法”的方式显式的调用父类中
* 声明的属性或方法。但是,通常情况下,我们习惯省略“super.”
* 3.2特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显
* 式的使用“super.属性”的方式,表明调用的是父类中声明的属性
* 3.3特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用“super.方法”的方式,表明调用的是父类中被重写的方法
* 4.super调用构造器
* 4.1我们可以在子类构造器中显式的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器
* 4.2“super(形参列表)”的使用,必须声明在子类构造器的首行!
* 4.3我们在类的构造器中,针对于“this(形参列表)”或“super(形参列表)”只能二选一,不能同时出现
* 4.4在构造器的首行,没有显式的声明“this(形参列表)”或“super(形参列表)”,则默认调用的是父类中的空参构造器:super();
* 4.5在类的多个构造器中,至少有一个类的构造器中使用了“super(形参列表)”,调用父类中的构造器
五、面向对象特征之三:多态性(Polymorphism)
* 1.理解多态性:可以理解为一个事物的多种形态
* 2.何为多态性:
* 对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
* 3.多态的使用:虚拟方法调用
* 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法
* 总结:编译看左边,运行看右边
* 4.多态性的使用前提
* ①要有类的继承关系
* ②要有方法的重写
* 5.对象的多态性只适用于方法,不适用于属性(编译和运行都看左边)
//考查多态的笔试题目:
public class InterviewTest2 {
public static void main(String[] args) {
Base2 base = new Sub2();
base.add(1, 2, 3);
// Sub2 s = (Sub2)base;
// s.add(1,2,3);
}
}
class Base2 {
public void add(int a, int... arr) {
System.out.println("base2");
}
}
class Sub2 extends Base2 {
public void add(int a, int[] arr) {//可以看做对父类方法的重写(int[] arr == int...arr)
System.out.println("sub_1");
}
// public void add(int a, int b, int c) {//不能看做对父类方法的重写
// System.out.println("sub_2");
// }
}
注:
* 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,
* 系统将不可能把父类里的方法转移到子类中(编译看左边,运行看右边)
* 2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,
* 这个实例变量依然不可能覆盖父类中定义的实例变量(编译运行都看左边)
注:数组也作为Object类的子类出现,可以调用Object类中声明的方法
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);
}
}
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();//20
}
}
面试题:
1.谈谈你对多态性的理解
①实现代码的通用性。
②Object类中定义的public boolean equals(Object obj){}
JDBC:使用java程序操作(获取数库连接、CURD)数据库(MySQL、Oracle、DB2、SQL Server)
③抽象类、接口的使用肯定体现了多态性(抽象类和接口不能实例化)
2.多态是编译时行为还是运行时行为
答:运行时行为
//面试题:多态是编译时行为还是运行时行为?
// 运行时行为
//证明如下:
class Animals{
protected void eat() {
System.out.println("animal eat food");
}
}
class Cat1 extends Animals{
protected void eat() {
System.out.println("cat eat fish");
}
}
class Dog1 extends Animals {
public void eat() {
System.out.println("Dog eat bone");
}
}
class Sheep extends Animals {
public void eat() {
System.out.println("Sheep eat grass");
}
}
public class InterviewTest {
public static Animals getInstance(int key) {
switch (key) {
case 0:
return new Cat1 ();
case 1:
return new Dog1 ();
default:
return new Sheep();
}
}
public static void main(String[] args) {
int key = new Random().nextInt(3);
System.out.println(key);
Animals animals = getInstance(key);
animals.eat();
}
}
* 有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类中特有的属性和方法不能调用
*
* 如何才能调用子类特有的属性和方法?
* 向下转型:使用强制类型转换符(多态性就相当于向上转型)
* 注意:使用强转时,可能出现ClassCastException的异常,因此引入关键字instanceof
instanceof关键字的使用
* a instanceof A:判断对象a是否是类A的实例。如果是,返回true、如果不是,返回false
* 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前、 先进行instanceof的判断,返回true就向下转型,返回false就不进行向下转型
* 如果 a instanceof A 返回true,则 a intsanceof B 也返回true。其中,类B是类A的父类
要求a所属的类与类A必须是子类和父类的关系,否则编译错误
public class PersonTest {
public static void main(String[] args){
Person p1 = new Man();
p1.eat();
p1.walk();
//不能调用子类所特有的方法、属性:编译时,p1是Person类型。
p1.name = "Tom";
// p1.isSmoking = true;
// p1.earnMoney();
//有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明
//为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类中特有的属性和方法不能调用
//如何才能调用子类特有的属性和方法
Man m1 = (Man)p1;//使用强制类型转换符
m1.isSmoking = true;
m1.earnMoney();
//使用强转时,可能出现ClassCastException的异常
// Woman w1 = (Woman)p1;
// w1.goShopping();
//instanceof:
if(p1 instanceof Woman){
Woman w1 = (Woman)p1;
w1.goShopping();
System.out.println("********woman*********");
}
if(p1 instanceof Man){
Man m2 = (Man)p1;
m2.earnMoney();
System.out.println("********man*********");
}
if(p1 instanceof Person){
System.out.println("******person********");
}
if(p1 instanceof Object){
System.out.println("******object********");
}
//问题一:编译时通过,运行时不通过
// 1.
// Person p2 = new Woman();
// Man m3 = (Man)p2;
// 2.
// Person p4 = new Person();
// Man m4 = (Man)p4;
//问题二:编译时通过,运行时也通过
// Object obj = new Woman();
// Person p = (Person)obj;
//问题三:编译时不通过
// Man m5 = new Woman();
}
}
六、java.long.Object类
* 1.Object类是所有Java类的根父类
* 2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
* 3.Object类中的功能(属性、方法)就具有通用性
* 4.Object类只声明了一个空参构造器
* 属性:无
* 方法:equals() /toString() /getClass() /hashCode() /clone() /finalize()
* wait()、notify()、notifyAll()
* 面试题
* final(关键字)、finally(关键字)、finalize(方法名)的区别
面试题:==和equals的区别
* 1.== 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
* 2.equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;
* 我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多, 久而久之,形成了equals是比较值的错误观点
* 3.具体要看自定义类里有没有重写Object的equals方法来判断
* 4.通常情况下,重写equals方法,会比较类中的相应属性是否都相等
一、回顾 == 的使用
* == :运算符
* 1.可以使用在基本数据类型变量和引用数据类型变量中
* 2.如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等(不一定类型要相同)
* 如果比较的是引用数据类型变量:比较两个对象的地址值是否相同(即两个引用是否指向同一个对象实体)
* 补充: == 符号使用时,必须保证符号左右两边的变量类型一致
二、equals()方法的使用
* 1.是一个方法,而非运算符
* 2.只能适用于引用数据类型
* 3.Object类中equals的定义
* public boolean equals(Object obj) {
* return (this == obj);
* }
* 说明:Object类中定义的equals()和==的作用是相同的(比较两个对象的地址值是否相同)
* 4.像String、Date、File、包装类等都重写了Object类中的equals()方法
* 重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的“实体内容”是否相同
* 5.通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的“实体内容”是否相同,就需要对Object类中的equals()进行重写
* 重写的原则:比较两个对象的实体内容是否相同
public class EqualsTest {
public static void main(String[] args) {
//基本数据类型
int i = 10;
int j = 10;
double d = 10.0;
System.out.println(i==j);//true
System.out.println(i==d);//true(类型提升)
boolean b = true;
// System.out.println(i==b);//不能比
char c1 = 10;
System.out.println(i==c1);//true
char c2 = 'A';
char c3 = 65;
System.out.println(c2==c3);//true
//**************************************
//引用数据类型
Customer cust1 = new Customer("Tom",21);
Customer cust2 = new Customer("Tom",21);
System.out.println(cust1==cust2);//false
String str1 = new String("atwlm");
String str2 = new String("atwlm");
System.out.println(str1==str2);//false
System.out.println("**************************************");
System.out.println(cust1.equals(cust2));//false--(重写equals())-->true
System.out.println(str1.equals(str2));//true //equals被String类重写过
Date date1 = new Date(24973283);
Date date2 = new Date(24973283);
System.out.println(date1.equals(date2));//true //equals被String类重写过
}
}
class Customer{
private String name;
private int age;
public Customer(){
}
public Customer(String name,int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
//重写原则:比较两个对象的“实体内容”(即name和age)是否相同
//手动实现equals()的重写
// @Override
// public boolean equals(Object obj) {
// if (this == obj) {
// return true;
// }
// if(obj instanceof Customer){
// Customer cust = (Customer)obj;
// //比较两个对象的属性是否都相同
if(this.age == cust.age && this.name.equals(cust.name)){
return true;
}else{
return false;
}
// //或
// return this.age == cust.age && this.name.equals(cust.name);
// }
// return false;
// }
//自动生成equals()
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Customer other = (Customer) 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;
}
//手动实现toString()的重写
// @Override
// public String toString() {
// return "Customer[name =" + name + ",age = " + age + "]";
// }
//自动生成toString()
@Override
public String toString() {
return "Customer [name=" + name + ", age=" + age + "]";
}
}
Object类中toString()的使用
* 1.当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
* 2.Object类中toString()的定义:
* public String toString() {
* return getClass().getName() + "@" + Integer.toHexString(hashCode());
* }
* 3.像String,Date,File,包装类等都重写了Object类中的toString()方法
* 使得在调用对象的toString()时,返回实体内容信息
* 4.自定义类也可以重写toString()方法,当调用此方法时,返回对象的“实体内容”
public class ToStringTest {
public static void main(String[] args) {
Customer cust1 = new Customer("Tom",21);
// System.out.println(cust1.toString());//com.atwlm2.java2.Customer@15db9742(重写toString之前)
// System.out.println(cust1);//com.atwlm2.java2.Customer@15db9742(重写toString之前)
System.out.println(cust1.toString());//Customer[name =Tom,age = 21](重写toString之后)
System.out.println(cust1);//Customer[name =Tom,age = 21](重写toString之后)
String str = new String("MM");
System.out.println(str);//MM
Date date = new Date(2794328465L);
System.out.println(date);//Mon Feb 02 16:12:08 CST 1970
}
public void test(){
//特殊情况
String s ="abc";
s = null;
System.out.println(s);//null
System.out.println(s.toString());//出现NullPointerException
}
}
Java中的JUnit单元测试
* 步骤:
* 1.选中当前工程 - 右键选择:build path - add libraries - JUnit 4 - 下一步
* 2.创建Java类,进行单元测试
* 此时的Java类要求:①此类是public的 ②此类提供公共的无参构造器
* 3.此类中声明单元测试方法
* 此时的单元测试方法:方法的权限是public,没有返回值,没有形参
* 4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
* 5.声明好单元测试方法以后,就可以在方法体内测试相关的代码
* 6.写完代码以后,左键双击单元测试方法名,右键ran as - JUnit Test
*
* 说明:
* 1.如果执行结果没有任何异常:绿条
* 2.如果执行结果出现异常:红条
public class JUnitTest {
int num = 10;
@Test
public void testEquals(){
String s1 = "MM";
String s2 = "MM";
System.out.println(s1.equals(s2));
//ClassCastException的异常
// Object obj = new String("GG");
// Date date = (Date)obj;
System.out.println(num);
show();
}
public void show(){
num = 20;
System.out.println("show()....");
}
@Test
public void testToString(){
String s2 = "MM";
System.out.println(s2.toString());
}
}
七、包装类的使用
* 1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
* 基本数据类型 包装类
* byte Byte
* short Short
* int Integer
* long Long
* float Float
* double Double
* boolean Boolean
* char Character
* 注意:其中Byte、Short、Integer、Long、Float、Double的父类是:Number
* 2.基本数据类型、包装类、String三者之间的相互转换。
自动装箱:基本数据类型--->包装类
int num2 = 10;
Integer in1 = num2;//自动装箱
boolean b1 = true;
Boolean b2 = b1;//自动装箱
自动拆箱:包装类--->基本数据类型
System.out.println(in1.toString());
int num3 = in1;//自动拆箱基本数据类型、包装类--->String类型:调用String重载的valueOf(Xxx xxx)
//方式一:连接运算
String str1 = num1 + "";
//方式二:调用String的valueOf(Xxx xxx)
float f1 = 12.3f;
String str2 = String.valueOf(f1);//"12.3"
System.out.println(str2);
String类型--->基本数据类型、包装类:调用包装类的parseXxx()
int num2 = Integer.parseInt(str1);
System.out.println(num2 + 1);//124
注意://可能会报NumberFormatException