高级类的特征: 继承与多态
一、高级类的特征:继承
1.继承性
①为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承这个类即可。
此处的多个类称为子类,单独的这个类称为父类( 基类或者超类 )。可以理解为“ 子类 is a 父类 ”
类继承语法规章:
class Subclass extends Superclass{ }
关键字: extends
②继承的作用
继承的出现提高了代码的复用性
继承的出现让类与类制间产生的关系,提供了多态的前提。
不要仅为了获取其他类中某个功能而去继承
示例:
public class Person {
int age;
String name;
int sex;
public void showInfo(){
System.out.println(this.age);
System.out.println(this.name);
System.out.println(this.sex);
}
}
public class Students extends Person{
String school;
public void showInfo(){
System.out.println(this.age);
System.out.println(this.name);
System.out.println(this.sex);
System.out.println(this.school);
}
}
总结&代码解析:
子类Students继承父类Person时,会继承父类Person的属性age,name,sex,所以当子类Students创建方法showInfo时可以直接用this.age等。 (this表示本类的属性,从而证明了Students类继承Person类后,得到了其属性作为本类的属性。)
public class Person {
int age;
String name;
int sex;
}
}
public class Students extends Person{
String school;
}
public class Workers extends Person{
String work;
}
public class Teachers extends Person{
String project;
}
总结&代码解析:
Students类,Teachers类,Workers类都具有年龄,名字,性别的属性,因此他们都可以继承父类Person的年龄,名字,性别,然后再在自己的类里写自己独特的属性
①继承就是把各类共同的东西抽出来形成父类实际需求的子类在继承父类的基础上写自己特有的代码即可。
②在子类中可以使用父类定义的方法和属性,也可以创建新的数据和方法。
③关键字是 extends ,即子类是父类的拓展而不是子集。
④子类不能访问父类私有的(private)的成员变量和方法。
2.单继承与多层继承
Java只支持单继承,不允许多重继承。
一个子类只能有一个父类
一个父类可以派生出多个子类
class SubDemo extends Demo{ }//ok
class SubDemo extends Demo1,Demo2…{ }//error
单继承与多层继承举例:
此代码中,Graduate类继承Students类,Students类继承Person类,因此Graduate类也是Person类的子类,同样继承Person类的属性和方法。
3.方法的重写(override)
定义: 在子类中可以根据需要从父类中继承来的方法进行改造,也称方法的重置、覆盖。在执行层序时,子类的方法将覆盖父类的方法。
父类的方法,想修改逻辑,但是有别的代码在调用父类的方法,可以考虑用子类继承父类,重新父类的方法。
要求:
①重写的方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型。其实就是子类重写父类的方法,实际上只是重新编写方法体的代码。
②重写方法不能使用比被重写方法更严格的访问权限。
③重写和被重写的方法必须同时为static的,或同时为非static的。
④子类方法抛出的以唱不能大于父类被重写方法 的异常
回顾方法的重写与重载
方法的重载-----一个类可以拥有多个同名方法。
方法的重写-----子类可以重新写父类的方法,覆盖父类的方法。
代码示例:
/*
定义类Kids,在Kids中重新定义employed()方法,
覆盖父类ManKind中定义的employed()方法,输出
“Kids should study and nojob."
*/
public class ManKind {
int age;
public static void employed(){
System.out.println("mankind need wook");
}
}
public class Kids extends ManKind {
//override
public static void employed(){
System.out.println("kid should study");
}
public static void main(String[] args) {
Kids somekids = new Kids();
somekids.employed();
}
}
输出结果为:kid should study
4.四种访问权限的修饰符
1.private关键字的特点:
1.被private修饰后,只有本类可以使用
2.private可以修饰成员变量和成员方法
2.default关键字的特点:
1.被default修饰后只能被类内部和同一个包里使用,注意不可被子类使用
2.default也可以修饰成员变量和成员方法
3.protected关键字的特点:
1.被protected修饰后能被类内部和同一个包里使用和被子类使用
4.public关键字的特点:
1.public可以在任意地方被使用
同时注意对于class的修饰权限只可以用public和default
public class a
{
}
class b
{
}
5.关键字super
super关键字:
①super可用于访问父类中定义的属性。
②super可用于调用父类中定义的成员方法。
③super可用于在子类构造方法中调用父类的构造器。
注意:
尤其当子父类出现同名成员时,可以用super进行区分。
super的追溯不仅限于直接父类。
super和this的用法很像,this代表本类对象的引用,super代表父类的内存空间的标识。
使用super子类可以调用子类之上的所有父类层级。
super调用父类构造器
①子类中所有的构造器默认都会访问父类中空参数的构造器。
②当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器,且必须放在构造器的第一行。
③如果子类构造器中既未显示调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错。
代码示例①:
public class ManKind {
public ManKind(){
System.out.println("Mankind 显示空参构造");
}
int age;
// public static void employed(){
// System.out.println("mankind need wook");
// }
}
public class Kids extends ManKind {
// public Kids(){
// super();
// }
public static void main(String[] args) {
Kids somekids = new Kids();
}
}
结果为:Mankind 显示空参构造
public class Kids extends ManKind {
public Kids(){
super();
}
public static void main(String[] args) {
Kids somekids = new Kids();
}
}
结果为:Mankind 显示空参构造
代码示例②:
public class ManKind {
int age;
int sex;
//构建有参构造器,当有有参构造器时,就不会默认给出无参构造器
public ManKind(int age, int sex) {
this.age = age;
this.sex = sex;
}
public class Kids extends ManKind {
public Kids(){
/显示报错
}
public static void main(String[] args) {
Kids somekids = new Kids();
}
}
所以子类的构造器必须通过this(参数列表)
//或者super(参数列表)语句指定调用本类或者父类中相应的构造器
public class Kids extends ManKind {
public Kids(int age, int sex){
//必须也有参数
super(age, sex);
}
public static void main(String[] args) {
Kids somekids = new Kids(9,0);
}
}
继承和super的整体使用题目:
1、写一个名为Account的类模拟账户。该类的属性和方法如下图所示。该类包括的属性:账号id,余额balance,年利率 annuallnterestRate;
包含的方法:访问器方法(getter和setter方法),返回月利率的方法 getMonthlyInterest(),取款方法 withdraw(),存款方法deposit()。
写一个用户程序测试Account类。在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%的Account对象。使用withdraw方法提款30000元,并打印余额。
再使用withdraw方法提款2500元,使用deposit方法存款3000元,然后打印余额和月利率。
提示:在提款方法 withdraw中,需要判断用户余额是否能够满足提款数额的要求,如果不能,应给出提示。
运行结果如图所示:
代码:
public class Account {
private int id;//账号
private double balance;//余额
private double annuallnterestRate;//年利率
public Account() {
}
public Account(int id, double balance, double annuallnterestRate) {
this.id = id;
this.balance = balance;
this.annuallnterestRate = annuallnterestRate;
}
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 getAnnuallnterestRate() {
return annuallnterestRate;
}
public void setAnnuallnterestRate(double annuallnterestRate) {
this.annuallnterestRate = annuallnterestRate;
}
public double getMonthlyinterest(){
//月利率
return annuallnterestRate/12;
}
public void withdraw(double amount){
//取款方法
if (amount >= balance) {
System.out.println("取款失败");
System.out.println("您的余额为:" + balance);
}
else {
this.balance = balance - amount;
System.out.println("取款成功");
System.out.println("您的余额为:" + this.balance);
}
}
public void deposit(double amount){
//存款方法
this.balance = balance + amount;
System.out.println("存款成功");
System.out.println("您的余额为:" + this.balance);
}
}
/*
*写一个用户程序测试Account类。在用户程序中,
*创建一个账号为1122、余额为20000、年利率4.5%的Account对象。
*使用withdraw方法提款30000元,并打印余额。
再使用withdraw方法提款2500元使用deposit方法存款30日日元,然后打印余额和月利率。
*/
public class AccountTest {
public static void main(String[] args) {
Account AC = new Account(1122,20000,0.045);
AC.withdraw(30000);
AC.withdraw(2500);
AC.deposit(30);
System.out.println("月利率为:" + AC.getMonthlyinterest());
}
}
//
输出结果:取款失败
您的余额为:20000.0
取款成功
您的余额为:17500.0
存款成功
您的余额为:17530.0
月利率为:0.00375
///
2、创建Account类的一个子类 CheckAccount 代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额。在CheckAccount类中重写withdraw方法,其算法如下:
如果〈取款金额<账户余额>)
可直接取款
如果(取款金额>账户余额>)
计算需要透支的额度
判断可透支额overdraf是否足够支付本次透支需要,如果可以
将账户余额修改为0,冲减可透支金额·
如果不可以
提示用户超过可透支额的限额
要求:
写一个用户程序测试CheckAccount类。在用户程序中,创建一个账号为1122、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象。
使用withdraw方法提款5000元,并打印账户余额和可透支额。
再使用withdraw方法提款18000元,并打印账户余额和可透支额。
再使用withdraw方法提款3000元,并打印账户余额和可透支额。
代码:
public class CheckAccount extends Account {
private double overdraft;
//子类构造器
public CheckAccount(int id, double balance, double annuallnterestRate, double overdraft) {
super(id, balance, annuallnterestRate);
this.overdraft = overdraft;
}
public double getOverdraft() {
return overdraft;
}
public void setOverdraft(double overdraft) {
this.overdraft = overdraft;
}
@Override
public void withdraw(double amount) {
if (amount < getBalance()){
super.withdraw(amount);//调用父类方法withdraw
}
else if (amount > getBalance()){
double OverDraft = amount - getBalance();
if (OverDraft < overdraft){
setBalance(0);
System.out.println("您的余额为:" + getBalance());
this.overdraft -= OverDraft;
}
else System.out.println("超过可透支额度限额");
}
}
}
/*
写一个用户程序测试CheckAccount类。在用户程序中,
创建一个账号为1122、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象。
使用withdraw方法提款5000元,并打印账户余额和可透支额。
再使用withdraw方法提款18000元,并打印账户余额和可透支额。
再使用withdraw方法提款3000元,并打印账户余额和可透支额。
*/
public class CheckAccountTeat {
public static void main(String[] args) {
CheckAccount acct = new CheckAccount(1122,20000,0.045,5000);
acct.withdraw(5000);
System.out.println("可透支额度为:" + acct.getOverdraft());
acct.withdraw(18000);
System.out.println("可透支额度为:" + acct.getOverdraft());
acct.withdraw(3000);
System.out.println("可透支额度为:" + acct.getOverdraft());
}
}
//输出结果为
取款成功
您的余额为:15000.0
可透支额度为:5000.0
您的余额为:0.0
可透支额度为:2000.0
超过可透支额度限额
可透支额度为:2000.0
二、高级类的特征:多态
1.多态性
①多态性是面向对象中最重要的概念,在java中有两种体现:
1.方法的重载(overload)和重写(overwrite)。
重载(overload):本类中的同名方法,体现相同名称的方法实现不同的逻辑。
重写(overwrite):子类对父类方法的覆盖,子类可以使用与父类相同的方法名,覆盖掉父类的逻辑。
父类的方法,想修改逻辑,但是有别的代码在调用父类的方法,可以考虑用子类继承父类,重新父类的方法。
2.对象的多态性一可以直接应用在抽象类和接口上。
②Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
若编译时类型和运行时类型不一致,就出现多态(Polymorphism)
---------------------这就是对象的多态
③对象的多态―在Java中,子类的对象可以替代父类的对象使用
一个变量只能有一种确定的数据类型
一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student();
Person e = new Student(); / / Person类型的变量e,指向student类型的对象
④子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
向上转型----把子类的对象可以给父类的类型引用。
代码:
问题:当前这个引用对象e指向的是哪个实例对象?
public class Students extends Person{
String school;
public void showInfo(){
System.out.println(this.age);
System.out.println(this.name);
System.out.println(this.sex);
System.out.println(this.school);
}
public static void main(String[] args) {
Person p = new Person();
Students s = new Students();
//以上是正常情况
Person per = new Students();//父类的引用对象可以指向子类的实例
Person e = new Person();
e = new Students();
//问题?--当前这个引用对象e指向的是哪个实例对象---Students
}
}
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student();
m.school =“pku";//合法,Student类有school成员变量
Person e = new Student();
e.school =“pku”;//非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。
2.虚拟方法调用(virtual Method Invocation)
正常的方法调用:
Person p = new Person();
p.getlnfo();
Student s = new Student();
s.getlnfo();
虚拟方法调用(多态情况下):
Person e = new Student();
e.getlnfo();/调用student类的getlnfo()方法
编译时类型和运行时类型:
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是student类的getlnfo()方法。——动态绑定
在堆中创建的方法是Students方法,所以e调用的是Students方法。
代码:
public class Person {
int age;
String name;
int sex;
public void showInfo(){
System.out.println("以下是Person类的shouinfo方法");
// System.out.println(this.age);
// System.out.println(this.name);
// System.out.println(this.sex);
}
}
public class Students extends Person{
String school;
public void showInfo(){
System.out.println("以下是Students类对Person类重写的shouinfo方法");
// System.out.println(this.age);
// System.out.println(this.name);
// System.out.println(this.sex);
// System.out.println(this.school);
}
public static void main(String[] args) {
Person p = new Person();
p.showInfo();
Students s = new Students();
s.showInfo();
//正常情况下
System.out.println("-------------");
Person e = new Students();
e.showInfo();
//虚拟方法调用(多态情况下)
}
}
//输出结果为
以下是Person类的shouinfo方法
以下是Students类对Person类重写的shouinfo方法
-------------
以下是Students类对Person类重写的shouinfo方法
多态小结
前提:
①需要存在继承或者实现关系
②要有覆盖操作
③成员方法的多态性,也就是动态绑定,必须得存在于方法的重写之上
成员方法:
编译时:要查看引用变量所属的类中是否有所调用的方法。
运行时:调用实际对象所属的类中的重写方法。
成员变量:
不具备多态性,只看引用变量所属的类。
子类继承父类
①若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中
②对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
关于①: 当子类没有重写方法时,多态将直接调用父类
Person p = new Person();
p.showInfo();
//Students s = new Students();
//s.showInfo();
//正常情况下
System.out.println("-------------");
Person e = new Students();
e.showInfo();
//虚拟方法调用(多态情况下)
///代码结果
以下是Person类的shouinfo方法
-------------
以下是Person类的shouinfo方法
3.多态性的运用
①方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法-------把子类看出一个特殊的父类
public class Test{
public void method(Person e) {
//......
e.getlnfo();
}
public static void main(Stirng args[]){
Test t = new Test();
Student m = new Student();
t.method(m);//子类的对象m传送给父类类型的参数e
}
② 定义三个类,父类Geometricobject代表几何形状,子类Circle代表圆形,MyRectangle代表矩形。定义一个测试类GeometricTest,
- 编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术),
- 编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
父类GeometricObject
public class GeometricObject {
//几何图形
protected String color;
protected double weight;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public GeometricObject(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
public double findArea(){
return 0.0;
}
}
///子类MyRectangle
public class MyRectangle extends GeometricObject {
private double width;
private double height;
public MyRectangle(double width,double height,String color, double weight) {
super(color, weight);
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public double findArea() {
return width * height;
}
}
///子类Circle
public class Circle extends GeometricObject {
private double radius;
public Circle(double radius,String color, double weight) {
super(color, weight);
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double findArea(){
return 3.14 * radius * radius;
}
}
///主方法
/*
*
* 定义一个测试类GeometricTest,
* 编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术),
* 编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
*/
public class GeometricTest {
public static void main(String[] args) {
GeometricTest test = new GeometricTest();
Circle c1 = new Circle(2.4, "white", 1.0);
test.displayGeometricObject(c1);
Circle c2 = new Circle(3.3, "white", 1.0);
test.displayGeometricObject(c2);
boolean isEquals = test.equalsArea(c1, c2);
System.out.println("c1 和 c2的面积是否相等:" + isEquals);
MyRectangle rect = new MyRectangle(2.1, 3.4, "red", 2.0);
test.displayGeometricObject(rect);
}
public void displayGeometricObject(GeometricObject o){
//GeometricObject o = new Circle(...) 这里就是多态
System.out.println("面积为:" + o.findArea());
}
//测试两个对象的面积是否相等
public boolean equalsArea(GeometricObject o1,GeometricObject o2){
return o1.findArea() == o2.findArea();
}
}
代码分析:GeometricTest中的displayGeometricObject的方法定义传递的参数是GeometricObject,是父类,而当传入实参时是GeometricObject的子类,等同于GeometricObject o = new Circle(…),这里就运用了多态,在代码编写中,出现定义传递参数是父类时,基本都使用了多态。
4.instanceof操作符
x instanceof A:检验x是否为类A的对象,返回值为boolean型。
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果x属于类A的子类B,x instanceof A值也为true。
其实就是检验某个对象是不是类A的子类
public class Person extends Object {
...}
public class Student extends Person {
...}
public class Graduate extends Person {
...}
---------------------------------------------------------
student s =new student ();
Person p = new Person ();
system.out.println (s instanceof Person);//ture
System.out.println (p instanceof Person);//ture
system.out.println (p instanceof student);//false
Person e =new student () ;
System. out.println (e instanceof student);//ture
/e是指向Students的实例的,所以结果固然为ture
三、Object类、包装类
1.Object对象
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类
public class Person {
…
}
等价于:
public class Person extends Object {
…
}
例:method(Object obj){…}//可以接收任何类作为其参数
Person o=new Person();
method(o);
问题:
想给test方法设置一个形参参数,这个参数我不确定到底会传进来一个什么类可以确定的是传递实参一定会是一个类,那么test方法的形参要设置一个什么类型?
public class Test {
/**
* 问题,想给test方法设置一个形参参数,这个参数我不确定到底会传进来一个什么类
* 可以确定的是传递实参一定会是一个类,那么test方法的形参要设置一个什么类型?
*/
public void test(Object obj){
}
public static void main(String[] args) {
Test t = new Test();
Person p = new Person();
Student s = new Student();
t.test(p);
t.test(s);
t.test(new Kk());//直接隐式变量,不用新创建一个对象
Person e = new Person();
e = p;
System.out.println(p.equals(e));
p.hashCode();
Object o = new Object();
System.out.println(o.hashCode());
System.out.println(p.toString());
}
}
为什么p对象可以执行object的方法?
object是所有类的父类,子类可以执行父类的方法,所以Person的对象p可以执行object的方法
Object类中的主要方法
2.对象类型转换
基本数据类型的Casting:
- 自动类型转换:小的数据类型可以自动转换成大的数据类型
如long g=20; double d=12.0f - 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型
如 float f=(float)12.0; int a=(int)1200L
对Java对象的强制类型转换称为造型
- 从子类到父类的类型转换可以自动进行
- 从父类到子类的类型转换必须通过造型(强制类型转换)实现
- 无继承关系的引用类型间的转换是非法的
public class Test {
public static void main(String[] args) {
int i = 10;
long l = i;
long l = 10l;
int i = (int)l;
Student s = new Student();
Person p = s;//从子类到父类的类型转换可以自动进行
Person p = new Person();
Student s = (Student) p;//从父类到子类的类型转换必须通过造型(强制类型转换)实现
Test t = new Test();
Person p1 = t;//无继承关系的引用类型间的转换是非法的
}
}
注意Object是所有类的最高父类
Object是所有类的最高父类
String s = "hello";
Object obj = s;//从子类到父类的类型转换可以自动进行
System.out.println(obj);
Object obj = "hello";
String s = (String) obj;//从父类到子类的类型转换必须通过造型(强制类型转换)实现
例子需求:我写了一个test类,里面有一个方法形参是Person,我想调用e.School方法,但School方法是Students的,然后判断e是不是Students的,如果是Students的就强转化为Students类型。
代码例子:
public class Student extends Person{
public void getSchool(){
System.out.println("这是student的getSchool方法");
}
}
public class Person {
String name;
public void test(){
System.out.println("这是person的test方法");
}
}
public class Test{
public void method(Person e) {
//设Person类中没有getschool() 方法
// e.getschool(); //非法,编译时错误
if(e instanceof Student){
Student s = (Student)e; //将e强制转换为Student类型
s.getschool();
}
else
{
e.test();
}
}
public static void main(Stirng args[]){
Test t = new Test();
t.method(new Student());
t.method(new Person());
}
}
/第一次输出这是student的getSchool方法
/第二次输出这是person的test方法
代码分析:第一次t.method(new Student( ));传入实参为Students对象,进入循环,Students属于Students,所以将形参Person e 的引用对象强转换为Students,从而可以使用getSchool方法。 第二次t.method(new Person( ));进入循环判断不是Students对象所以直接输出teat方法。
判断最好用instanceof
3.= =符与equals
= =:
①基本类型比较值:只要两个变量的值相等,即为true.
int a=5; if(a==6){…}
②引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,= =才返回true.
Person p1=new Person();
Person p2=new Person();
if (p1= =p2){…}
③用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错;
第②类型的代码示例:
Person p1 = new Person();
Person p2 = p1;
System.out.println(p1 == p2);//只有指向同一个对象时,==才返回true.
System.out.println(p1.equals(p2));//equals只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。
Person p1 = new Person();
Person p2 = new Person();
System.out.println(p1 == p2);//显示flase
第③类型的代码示例:
Test t = new Test();
System.out.println(p1 == t);//报错!!用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错
equals()
①所有类都继承了Object,也就获得了equals()方法。还可以重写。
- 只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。
- 格式:obj1.equals(obj2)
②特例:当用equals()方法进行比较时,对类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象;
原因:在这些类中重写了Object类的equals()方法。
Person p1 = new Person();
Person p2 = new Person();
System.out.println(p1 == p2);//flase!!!只有指向同一个对象时,==才返回true.
System.out.println(p1.equals(p2));//false!!!equals只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。
特例的演示代码:
String s1 = new String("abc");
String s2 = new String("abc");
String s3 = "abc";
System.out.println(s1 == s2);//false!! 不是同一对象
System.out.println(s1.equals(s2));//ture!! 比较类型及内容而不考虑引用的是否是同一个对象
总结
1.对于对象来说:
特殊的类,如String、File、Date,使用= =比较的是对象(对象的地址),equals比较内容。
2.除了特殊的类之外的其他普通的类的对象,= =和equals比较的都是对象(对象的内存地址)。
3.如果你想改变某一个类的equals,不想用equals来比较对象的内存地址,则需要重写equals方法。
练习:
1.编写Order类,有int型的orderId,String型的OrderName,相应的getter()和setter()方法,两个参数的构造器,重写父类的equals()方法:public boolean equals(Object obj),并判断测试类中创建的两个对象是否相等。
public class Order {
public Order(int orderId, String orderName){
this.orderId = orderId;
this.orderName = orderName;
}
int orderId;
String orderName;
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
@Override
public boolean equals(Object obj) {
boolean flag = false;
if(obj instanceof Order){
Order o = (Order) obj;
if(this.orderId == o.orderId && this.orderName.equals(o.orderName)){
flag = true;
}
}
return flag;
}
}
/测试代码
Order o1 = new Order(123, "a001");
Order o2 = new Order(123, "a002");
System.out.println(o1.equals(o2));
2.请根据以下代码自行定义能满足需要的MyDate类,在MyDate类中覆盖equals方法,使其判断当两个MyDate类型对象的年月日都相同时,结果为true,否则为false。 public boolean equals(Object o)
public class MyDate {
public MyDate(int year,int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
int year;
int month;
int day;
@Override
public boolean equals(Object obj) {
int flag = 1;
if(obj instanceof MyDate){
MyDate md = (MyDate) obj;
flag = 0;
if(this.year != md.year){
flag += 1;
}
if(this.month != md.month){
flag += 1;
}
if(this.day != md.day){
flag += 1;
}
}
if(flag == 0){
return true;
}else{
return false;
}
}
}
/测试代码
MyDate m1 = new MyDate(2030,11,23);
MyDate m2 = new MyDate(2050,12,23);
System.out.println(m1.equals(m2));
代码分析: public boolean equals(Object obj) 方法,首先定义int flag等于1,假如传进来的不是Mydate类,则if后面都不用执行,假如是Mydate类,则有一项相同就加1,只有三项都不想同才为0,这样代码简洁巧妙
4.包装类(Wrapper)
- 针对八种基本定义相应的引用类型—包装类(封装类)
- 有了类的特点,就可以调用类中的方法。
①基本数据类型包装成包装类的实例 — 装箱
- 通过包装类的构造器实现:
int i = 500; Integer t = new Integer(i); - 还可以通过字符串参数构造包装类对象:
Float f = new Float(“4.56”);
Long l = new Long(“asdf”); //NumberFormatException
②获得包装类对象中包装的基本类型变量 — 拆箱
调用包装类的.xxxValue()方法:
Integer i = new Integer(111);
Integer i = new Integer("123");
Integer i = new Integer("abc");//这样的编译是不报错,运行有错误
System.out.println(i);
Integer i = new Integer(112);
int i0 = i.intValue();
③JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。
Integer i = new Integer(112);
int i0 = i.intValue();
Integer i1 = 112;//自动装箱
int i2 = i1;//自动拆箱
System.out.println(i0);
boolean b = new Boolean("false");//自动拆箱
Boolean b1 = true;//自动装箱
System.out.println(b);
④字符串转换成基本数据类型
通过包装类的构造器实现:
int i = new Integer(“12”);
通过包装类的parseXxx(String s)静态方法:
Float f = Float.parseFloat(“12.1”);
int i = Integer.parseInt("123");
float f = Float.parseFloat("0.40");
boolean b = Boolean.parseBoolean("false");
⑤基本数据类型转换成字符串
调用字符串重载的valueOf()方法:
String fstr = String.valueOf(2.34f);
更直接的方式:
String intStr = 5 + “”
String istr = String.valueOf(i);
String fstr = String.valueOf(f);
String bstr = String.valueOf(true);
基本数据类的包装类主要是基本数据类型和字符串的直接转换
5.to String
-
父类Object的toString方法就输出当前对象的内存地址
-
如果要你想要输出类的其他信息,重写toString方法
MyDate m = new MyDate(2030, 12, 12);
System.out.println(m.toString());//打印其内存地址
System.out.println(m);//也是打印其内存地址
重写MyDate类to String方法 ,让他可以输出年月日
@Override
public String toString() {
String str = this.year + "-" + this.month + "-" + this.day;
return str;
}
MyDate m = new MyDate(2030, 12, 12);
System.out.println(m.toString());//打印其内存地址
System.out.println(m);//打印m对象相当于执行打印m.toString(),这个在其他对象也是如此,
/输出结果:
2030-12-12
203012-12
System.out.println(m.toString());//打印其内存地址
System.out.println(m);//打印m对象相当于执行打印m.toString(),这个在其他对象也是如此,