文章目录
8、面向对象编程(中)
8.1、面向对象特征之二:继承性
package com.test.dm03;
/*
* 一、继承性的好处:
* ① 减少了代码的冗余,提供了代码的复用性
* ② 便于功能的扩展
* ③ 为之后多态性的使用,提供了前提
*
* 二、继承性的格式:
* class A extends B{}
* A:子类、派生类、subclass
* B:父类、超类、基类、superclass
*
* 2.1、体现:
* 一旦子类A继承父类B以后,子类A中就获取了父类B中的声明的结构: 属性、方法
* 所有的属性和方法。
* 特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。
*
* 只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
*
*
* 2.2、子类继承父类以后,还可以声明自己特有的属性或方法:
* 实现功能的拓展。
* 子类和父类的关系,不同于子集和集合的关系。
* extends: 延展、扩展
* 三、Java中关于继承性的规定:
* 1、一个类可以被多个子类继承。
* 2、Java中类的单继承性:一个类只能有一个父类。
* 3、子父类是相对的概念。父类的父类是间接父类。
* 4、子类直接继承的父类,称为:直接父类。
* 间接继承的父类称为:间接父类
* 5、子类继承父类以后,就获取了父类以及所有间接父类中声明的属性和方法。
*
*/
public class ExtendsTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.age = 1;
Student s1 = new Student();
s1.setId(10);
s1.toString();
s1.eat();
}
}
Object类的理解:
光标放在类上Ctrl+T即可查看继承树
/ *
* 四、Object类的理解:
* 1、如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
* 2、所有的Java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
* 3、意味着,所有的Java类具有java.lang.Object类声明的功能。
8.2、Eclipse Debug的使用
操作 | 作用 |
---|---|
step into 跳入(F5) | 进入当前行所调用的方法中 |
step over 跳过(F6) | 执行完当前行的语句,进入下一行 |
step return 跳回(F7) | 执行完当前行所在的方法,进入下一行 |
drop to frame | 回到当前行所在方法的第一行 |
resume 恢复 | 执行完当前行所在断点的所有代码,进入下一个断点,如果没有就结束 |
package com.test.dm03;
/*
* 如何调试程序:
* 1、System.out.println();
*
*
*
*/
public class DebugTest {
public static void main(String[] args) {
int[] arr = new int[] {1,2,3,4,5};
System.out.println(arr);
char[] arr1 = new char[] {'a','b','c'};
System.out.println(arr1);
}
}
//结果
[I@182decdb
abc
通过debug得知,调用的是这两个是不同的方法,所以结果不同。
Debug as 选择Run Confugurations…
选择JRE 选择 Alternate JRE:选择jdk里面的jre
选择后面的Installed JREs…
先remove再Add 点击JDK目录
8.3、方法的重写(override/overwrite)
定义:
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
- 子类不能重写父类中声明为private权限的方法
- 子类方法抛出的异常不能大于父类被重写方法的异常
注意:
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写),因为static方法是属于类的,子类无法覆盖父类的方法。
Person类
package com.test.dm03;
public class Person {
String name;
int age;
private int id;
public Person() {
}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃饭");
}
public void sleep() {
System.out.println("睡觉");
}
public void setId(int id) {
this.id = id;
}
public Object info() {
return null;
}
}
Student类
package com.test.dm03;
public class Student extends Person{
// String name;
// int age;
String major;
public Student() {
}
public Student(String major) {
this.major = major;
}
public Student(String name,int age,String major) {
this.name = name;
this.age = age;
this.major = major;
}
public void study() {
System.out.println("学习");
}
public void eat() {
System.out.println("学生应该多吃有营养的食物");
}//对父类中的eat()进行了重写
// public void sleep() {
// System.out.println("睡觉");
// }
public String info() {
return null;
}
}
PersonTest类
package com.test.dm03;
/*
* 方法的重写(override/overwrite)
*
* 1、重写: 子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
*
* 2、应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中同名同参数的方法时,实际执行的是子类重写父类的方法。
*
* 3、重写的规定:
* 方法的声明: 权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
* //方法体
* }
*
*
* 约定俗成:
* 子类中的叫重写的方法,父类中的叫被重写的方法
* ① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
* ② 子类重写的方法的权限修饰符 不小于父类被重写的方法的权限修饰符
* >特殊情况:
* 子类不能重写父类中声明为private权限的方法
*
* ③ 返回值类型:
* > 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
* > 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或者A类的子类。
* > 父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是:double)。
*
*
*
* ④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。(具体放到异常处理时候讲)
*
*
*
* ********************************************************************************************************
*
*
* 子类和父类中的同名同参数的 方法 要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
*
*
*
* 面试题:区分方法的重载与重写
*
*
*/
public class PersonTest {
public static void main(String[] args) {
Student s = new Student("计算机科学与技术");
s.eat();
}
}
8.4、四种访问权限修饰符
Java权限修饰符public、protected、private置于类的成员定义前,用来限定对象对该类成员的访问权限
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | yes | |||
(缺省) | yes | yes | ||
protected | yes | yes | yes | |
public | yes | yes | yes | yes |
对于class的权限修饰只可以用public和default(缺省)
- public类可以在任意地方被访问
- default类只可以被同一个包内部的类访问。
demo04包下
Order.java
package com.test.dm04;
/*
* 体会4种不同的权限修饰
*
*
*
*/
public class Order {
private int orderPrivate;
int orderDefault;
protected int orderProtected;
public int orderPublic;
private void methodPrivate() {
}
void methodDefault() {
}
protected void methodProtected() {
}
public void methodPublic() {
}
}
Ordertest.java
package com.test.dm04;
/*
* 体会4种不同的权限修饰
*
*
*
*/
public class Order {
private int orderPrivate;
int orderDefault;
protected int orderProtected;
public int orderPublic;
private void methodPrivate() {
}
void methodDefault() {
}
protected void methodProtected() {
}
public void methodPublic() {
}
}
demo05包下
OrderTest.java
package com.test.dm05;
import com.test.dm04.Order;
public class OrderTest {
public static void main(String[] args) {
Order order = new Order();
order.orderPublic = 1;
order.methodPublic();
//不同包下的普通类(非子类)要使用Order类, 不可调用声明为private、缺省、protected权限的属性、方法。
// order.orderPrivate = 1;
// order.orderDefault = 2;
// order.orderProtected = 3;
//
// order.methodPrivate();
// order.methodDefault();
// order.methodProtected();
//
}
}
SubOrder.java
package com.test.dm05;
import com.test.dm04.Order;
//不同包里
public class SubOrder extends Order {
public void method() {
orderProtected = 1;
orderPublic = 2;
methodProtected();
methodPublic();
// 在不同的包的子类中,不能调用Order类中声明为private和缺省权限的属性、方法
// orderDefault = 3;
// orderPrivate = 4;
//
// methodDefault();
// methodPrivate();
}
}
8.5、super关键字的使用
Person.java
package com.test.dm05;
public class Person {
String name;
int age;
int id = 1001;//身份证号
public Person() {
System.out.println("我无处不在!!!");
}
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 void sleep() {
System.out.println("睡觉");
}
public void setId(int id) {
this.id = id;
}
public Object info() {
return null;
}
}
Student.java
package com.test.dm05;
public class Student extends Person{
// String name;
// int age;
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 study() {
System.out.println("学生学习知识");
}
@Override
public void eat() {
System.out.println("学生应该多吃有营养的食物");
}
//对父类中的eat()进行了重写
// public void sleep() {
// System.out.println("睡觉");
// }
public String info() {
this.eat();
super.eat();
walk();
return null;
}
public void show() {
System.out.println("name = "+ this.name + ",age = " + super.age);
System.out.println("id = "+ id);//省略了this.id
System.out.println("id = "+ super.id);
}
}
SuperTest.java
package com.test.dm05;
/*
* 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(形参列表)”。调用父类中的构造器
*/
public class SuperTest {
public static void main(String[] args) {
Student s = new Student();
s.show();
System.out.println();
s.info();
Student s1 = new Student("Tom",21,"IT");
s1.show();
System.out.println("********************");
Student s2 = new Student();
}
}
8.6、子类对象实例化过程
package com.test.dm05;
/*
* 子类对象实例化的全过程
*
* 1、从结果上来看: (继承性)
* 子类继承父类以后,就获取了父类中声明的属性或方法。
* 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
*
* 2、从过程上来看:
* 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,
* 直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,
* 子类对象才考虑进行调用。
*
*
* 明确:
* 虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
*/
public class InstanceTest {
}
8.7、面向对象特征之三:多态性
Person.java
package com.test.dm06;
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 void sleep() {
System.out.println("睡觉");
}
public void setId(int id) {
this.id = id;
}
public Object info() {
return null;
}
}
Man.java
package com.test.dm06;
public class Man extends Person{
boolean isSmoking;
int id = 1002;
public void earnMoney() {
System.out.println("男人负责挣钱养家");
}
public void eat() {
System.out.println("吃肉");
}
public void walk() {
System.out.println("男人走路");
}
}
Woman.java
package com.test.dm06;
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("女人走路");
}
}
PersonTest.java
package com.test.dm06;
/*
*
* 面向对象特征之三:多态性
*
* 1、理解多态性,可以理解为一个事物的多种形态。
*
* 2、何为多态性:
* 对象多态性: 父类的引用指向子类的对象(或子类的对象赋给父类的引用)
*
* 3、多态的使用: 虚拟方法调用
* 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
*
* 总结:
* 编译,看左边,运行,看右边。
*
* 4、多态性的使用前提:
* ① 要有类的继承关系
* ② 要有方法的重写
*
* 5、对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
*/
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();
//***************************************************
System.out.println("**************************************");
//对象多态性: 父类的引用指向子类的对象
Person p2 = new Man();
// Person p3 = new Woman();
//多态的使用: 当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用
p2.eat();
p2.walk();
//p2.earnMoney();The method earnMoney() is undefined for the type Person
System.out.println(p2.id);
}
}
AnimalTest.java
package com.test.dm06;
//多态性的使用,举例一:
import java.sql.Connection;
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();
// }
// public void func(Cat cat) {
// cat.eat();
// cat.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 Order{
public void method(Object obj) {
}
}
//举例三:
class Driver{
public void doData(Connection conn) {//conn = new MySQLConnection();/ conn = new OracleConnection();
//规范的步骤去操作数据。
// conn.method1();
// conn.method2();
// conn.method3();
}
}
虚拟方法调用再理解
虚拟方法调用(Virtual Method Invocation)
正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚拟方法调用(多态情况下)
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
编译时类型和运行时类型
编译时e为Person类型,而方法的调用时在运行时确定的,所以调用的是Student类的getInfo()方法。 -------动态绑定
面试题: 多态是编译时行为还是运行时行为?
运行时行为
小结: 方法的重载和重写
1、二者的定义细节: 略
2、从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以: 对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为”早绑定“ 或 ”静态绑定“;
而对于多态,只有等到方法调用的那一刻,编译器才会确定所要调用的具体方法,这称为”晚绑定“ 或 ”动态绑定“
引用Bruce Eckel的话: ”不要犯傻,如果它不是晚绑定,它就不是多态。“
向下转型的使用
Person.java
package com.test.java;
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 void sleep() {
System.out.println("睡觉");
}
public void setId(int id) {
this.id = id;
}
public Object info() {
return null;
}
}
Man.java
package com.test.java;
public class Man extends Person{
boolean isSmoking;
int id = 1002;
public void earnMoney() {
System.out.println("男人负责挣钱养家");
}
public void eat() {
System.out.println("吃肉");
}
public void walk() {
System.out.println("男人走路");
}
}
Woman.java
package com.test.java;
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("女人走路");
}
}
PersonTest.java
package com.test.java;
/*
*
* 面向对象特征之三:多态性
*
* 1、理解多态性,可以理解为一个事物的多种形态。
*
* 2、何为多态性:
* 对象多态性: 父类的引用指向子类的对象(或子类的对象赋给父类的引用)
*
* 3、多态的使用: 虚拟方法调用
* 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
*
* 总结:
* 编译,看左边,运行,看右边。
*
* 4、多态性的使用前提:
* ① 要有类的继承关系
* ② 要有方法的重写
*
* 5、对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
*
* *****************************************************************
*
*
*
*/
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();
//***************************************************
System.out.println("**************************************");
//对象多态性: 父类的引用指向子类的对象
Person p2 = new Man();
// Person p3 = new Woman();
//多态的使用: 当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用
p2.eat();
p2.walk();
//p2.earnMoney();The method earnMoney() is undefined for the type Person
System.out.println(p2.id);
System.out.println("**************************************");
//不能调用子类所特有的方法、属性: 编译时,p2是Person类型。
//p2.earnMoney();//不可以
//有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致
//编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
//如何才能调用子类特有的属性和方法?
//向下转型,使用强制类型转换符
Man m1 = (Man)p2;
m1.earnMoney();
m1.isSmoking = true;
// class com.test.java.Man cannot be cast to class com.test.java.Woman
//使用强转时,可能出现ClassCastException异常
// Woman w1 = (Woman)p2;
// w1.goShopping();
//
//向上转型:即是多态,Person p2 = new Man();
/*
*
* instanceof:
*
* a instanceof A: 判断对象a是否时类A的实例。如果是,返回true;如果不是,返回false
*
* 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,
* 先进行instanceof的判断,一旦返回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 w1 = (Man)p2;
w1.earnMoney();
System.out.println("***Man");
}
if(p2 instanceof Person) {
System.out.println("***Person");
}
if(p2 instanceof Object) {
System.out.println("***Object");
}
}
}
8.8、Object类的使用
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
public class Person{
......
}
等价于:
public class Person extends Object{
......
}
//例:
method(Object obj){...}//可以接收任何类作为其参数
Person o = new Person();
method(o);
package com.test.java;
/*
*
* java.lang.Object类
* 1、Object类是所有Java类的根父类
* 2、如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
* 3、Object类中的功能(属性、方法)就具有通用性。
* 属性: 无
* 方法:
* equals() / toString() / getClass() / hashCode() / claone() / finalize()
* wait()、notify()、notifyAll()
*
*
* 4、Object类只声明了一个空参的构造器
*
* 面试题:
* final\finally\finalize(方法名)的区别:
*
*/
public class ObjectTest {
public static void main(String[] args) {
Order order = new Order();
System.out.println(order.getClass().getSuperclass());
}
}
class Order{//Ctrl + T 查看继承树
}
8.9、运算符的回顾 , 和 equals 的区别
面试题:
== 和 equals 的区别
1、== 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
2、equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。
3、具体要看自定义类里有没有重写Object的equals方法来判断。
4、通常情况下,重写equals方法,会比较类中的相应属性是否都相等。
Customer.java
package com.test.java;
public class Customer {
private String name;
private int age;
public Customer() {
super();
}
public Customer(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
}
EqualsTest.java
package com.test.java;
/*
*
* 面试题: == 和 equals() 区别
*
* 一、 回顾 == 的使用
* == : 运算符,
* 1、可以使用在基本数据类型变量和引用数据类型变量中
* 2、如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
* 如果比较的是引用数据类型变量:比较两个对象的 地址值是否相同
*
* 即,两个引用是否指向同一个对象实体
*
*
*/
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 c = 10;
System.out.println(i == c);
char c1 = 'A';
char c2 = 65;
System.out.println(c1 == c2);
Customer cust1 = new Customer("Tom",21);
Customer cust2 = new Customer("Tom",21);
System.out.println(cust1 == cust2);//false
String str1 = new String("aaa");
String str2 = new String("aaa");
System.out.println(str1 == str2);//false
}
}
8.10、equals()方法的使用
/*
* 二、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 c = 10;
System.out.println(i == c);
char c1 = 'A';
char c2 = 65;
System.out.println(c1 == c2);
Customer cust1 = new Customer("Tom",21);
Customer cust2 = new Customer("Tom",21);
System.out.println(cust1 == cust2);//false
String str1 = new String("aaa");
String str2 = new String("aaa");
System.out.println(str1 == str2);//false
System.out.println("***************");
System.out.println(cust1.equals(cust2));//false
//boolean java.lang.Object.equals(Object obj)
System.out.println(str1.equals(str2));//true
//boolean java.lang.String.equals(Object anObject)
Date date1 = new Date(32432525324L);
Date date2 = new Date(32432525324L);
System.out.println(date1 == date2);
System.out.println(date1.equals(date2));//true
}
}
重写equals()方法
Customer.java
package com.test.java;
public class Customer {
private String name;
private int age;
public Customer() {
super();
}
public Customer(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
//重写的原则: 比较两个对象的实体内容(即: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;
// }
//选项source下有自动生成equals()选项,快捷键:alt + shift + s
@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;
}
}
EqualsTest.java
package com.test.java;
import java.util.Date;
/*
*
* 面试题: == 和 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 c = 10;
System.out.println(i == c);
char c1 = 'A';
char c2 = 65;
System.out.println(c1 == c2);
Customer cust1 = new Customer("Tom",21);
Customer cust2 = new Customer("Tom",21);
System.out.println(cust1 == cust2);//false
String str1 = new String("aaa");
String str2 = new String("aaa");
System.out.println(str1 == str2);//false
System.out.println("***************");
System.out.println(cust1.equals(cust2));//false --> true(重写equals()方法后)
//boolean java.lang.Object.equals(Object obj)
System.out.println(str1.equals(str2));//true
//boolean java.lang.String.equals(Object anObject)
Date date1 = new Date(32432525324L);
Date date2 = new Date(32432525324L);
System.out.println(date1 == date2);
System.out.println(date1.equals(date2));//true
}
}
重写equals()方法的原则
-
对称性:
- 如果x.equals(y)返回的是"true",那么y.equals(x)也应该返回的是"true"
-
自反性:
- x.equals(x)必须返回是”true“
-
传递性:
- 如果x.equals(y)返回是"true",而且y.equals(z)返回是"true",
那么z.equals(x)也应该返回是"true"
-
一致性:
- 如果x.equals(y)返回是"true",只要x和y内容不变,不管你重复x.equals(y)
多少次,返回都是"true"
-
任何情况下,x.equals(null),永远返回是"false";
x.equals(和x不同类型的对象)永远返回是"false"
null.equals(x)空指针异常。
* 一、 回顾 == 的使用
* == : 运算符,
* 1、可以使用在基本数据类型变量和引用数据类型变量中
* 2、如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
* 如果比较的是引用数据类型变量:比较两个对象的 地址值是否相同
*
* 即,两个引用是否指向同一个对象实体
* 补充:
* == 符号使用时,必须保证符号左右两边的变量类型一致。
*
8.11、toString()方法的使用
toString()方法在Object类中定义,其返回值类型是String类型,返回类名和它的引用地址。
Customer.java
package com.test.java;
public class Customer {
private String name;
private int age;
public Customer() {
super();
}
public Customer(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
//重写的原则: 比较两个对象的实体内容(即: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;
// }
//选项source下有自动生成equals()选项,快捷键:alt + shift + s
@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;
}
// 手动实现的
// @Override
// public String toString() {
// return "Customer[name="+name+",age ="+age+"]";
//
// }
//自动实现的
@Override
public String toString() {
return "Customer [name=" + name + ", age=" + age + "]";
}
}
ToStringTest.java
package com.test.java;
import java.util.Date;
/*
* 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.test.java.Customer@26f0a63f
System.out.println(cust1);//com.test.java.Customer@26f0a63f
//---->重写后:Customer[name=Tom,age =21]
String str = new String("MM");
System.out.println(str);//MM
Date date = new Date(13212453L);
System.out.println(date.toString());
}
}
8.12、单元测试方法的使用
JUnitTest.java
package com.test.java1;
import java.util.Date;
import org.junit.Test;
/*
*
* Java中的JUnit单元测试
*
* 步骤:
*
* 1、选中当前工程 -- 右键选择:build path -- add libraries -- JUnit 4 -- 下一步
* 2、创建一个Java类,进行单元测试
* 此时的Java类要求:
* ① 此类是public 的
* ② 此类提供公共的无参的构造器
*
* 3、此类中声明单元测试方法
* 此时的单元测试方法:
* 方法的权限是public ,没有返回值,没有形参
*
* 4、此单元测试方法上需要声明注解: @Test , 并在单元测试类中导入import org.junit.Test;
*
*
*
* 5、声明好单元测试方法以后,就可以在方法体内测试相关的代码。
*
* 6、写完代码以后,左键双击单元测试方法名,右键: run as --- JUnit Test
* 只会执行双击的方法!!!
*
* 说明:
* 1、如果执行结果没有任何异常: 绿条
* 1、如果执行结果出现异常: 红条
*
* 也可以直接在方法中写@Test,会自动提示我们 add libraries
*/
public class JUnitTest {
int num = 10;
@Test
public void testEquals() {
String s1 = "MM";
String s2 = "MM";
System.out.println(s1.equals(s2));
// Object obj = new String("GG");
// Date date = (Date)obj;
//main方法中new 对象 才能调属性,原因是static的
//test方法中直接和非静态的普通方法一样调用即可
System.out.println(num);
show();
}
public void show() {
num = 20;
System.out.println("show()......");
}
}
8.13、包装类(Wrapper)的使用
针对八种基本数据类型定义相应的引用类型------包装类(封装类)
有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
Short、Integer、 Long、Float、Double(数值型)父类是Number
WrapperTest.java
package com.test.java1;
import org.junit.Test;
/*
*
* 包装类的使用:
* 1、Java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
*
* 2、掌握的:
* 基本数据类型、包装类、String三者之间的相互转换
*
*/
public class WrapperTest {
//基本数据类型 ---> 包装类,调用包装类的构造器
@Test
public void test1() {
int num1 = 10;
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
Integer in2 = new Integer("123");
System.out.println(in2.toString());
//java.lang.NumberFormatException: For input string: "123abc"
// 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");
System.out.println(b2);
Boolean b3 = new Boolean("true123");
System.out.println(b3);
Order order = new Order();
System.out.println(order.isMale);//false
System.out.println(order.isFemale);//null
}
//包装类------>基本数据类型:调用包装类Xxx的xxxValue()
@Test
public void test2() {
Integer in1 = new Integer(12);
int i1 = in1.intValue();
System.out.println(i1);
Float f1 = new Float(12.3f);
Float f2 = f1.floatValue();
System.out.println(f2);
}
}
class Order {
boolean isMale;
Boolean isFemale;
}
自动装箱与自动拆箱
/*
* JDK 5.0新特性: 自动装箱与自动拆箱
*
*/
@Test
public void test3() {
// int num1 = 10;
//
// //基本数据类型--->包装类的对象
//
//
// method(num1);
//
//自动装箱: 基本数据类型 ---> 包装类
int num2 = 10;
Integer in1 = num2;//自动装箱
boolean b1 = true;
Boolean b2 = b1;//自动装箱
//自动拆箱: 包装类------>基本数据类型
System.out.println(in1.toString());
int num3 = in1;
}
public void method(Object obj) {
System.out.println(obj);
}
基本数据类型包装类与String的相互转换
//String类型 ---> 基本数据类型、包装类:调用包装类的parseXxx()
@Test
public void test5() {
String str = "123";
//可能会报NumberFormatException
int num = Integer.parseInt(str);
System.out.println(num+1);
String str1 = "true";//大写也可转为true
boolean b1 = Boolean.parseBoolean(str1);
System.out.println(b1);
}
//基本数据类型、包装类 ---> String类型:调用String重载的valueOf(Xxx xxx)
@Test
public void test4() {
int num1 = 10;
//方式1:连接运算
String str1 = num1 + "";
//方式2:调用String重载的valueOf(Xxx xxx)
float f1 = 12.3f;
String str2 = String.valueOf(f1);//"12.3"
Double d1 = new Double(12.4);
String str3 = String.valueOf(d1);//"12.4"
System.out.println(str2);
System.out.println(str3);
}