继承
如果让你创建学生类你可能一下就能创建完,但是如果让你创建学生类,工人类,教师类,农民类,而且这些类的属性都有相同的属性和方法:姓名,年龄,性别,打印个人信息的方法。按照前面说的把代码写出来,如果直接这样去硬写,是不是代码的重复就很多。
Java提供了一个继承机制,可以解决这个问题。
首先创建一个人类,把要创建的类的相同属性放到人类里面,然后其他类只需要添加自己特有的属性和方法就行,其他的属性继承就行。
public class Person {//人类 ——父类 private int age; private String name; private String sex; //下面的get和set方法自动生成,右键source > Generate Getter and Setter public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } } class Students extends Person{ //学生类继承了人类 子类继承父类 注意一个类中只能有一个public修饰的类 private String school;//子类继承父类后,子类拥有父类的所有属性和方法,不必再次添加属性 public static void main(String[] args) { Students s = new Students();//创建学生对象 s.setName("张三");//调用了Person父类的方法 System.out.println(s.getName());//这里也是调用父类的方法打印名字 } }
不要为了获取其他类中的某些属性/方法/功能而去继承。比如创建一个狗类,狗也有姓名,年龄,性别。但是狗类可以继承人类吗,不可以。虽然语法上不会报错,但是思想上不符合逻辑。如果狗类想继承,可以创建一个动物类,然后狗类继承动物类。
继承需要逻辑关系在里面,不要随意继承。
练习1.1
//ManKind类 public class ManKind { int sex,salary;//定义属性 public void manOrWorman() {//判断男/女 if(this.sex==1) { System.out.println("man"); } else if(this.sex==0){ System.out.println("women"); } else { System.out.println("请输入正确的年龄数字"); } } public void employedd() {//判断是否有工作 if(this.salary==0) { System.out.println("no job"); } else if(this.salary!=0) { System.out.println("job"); } } } //Kids类 public class Kids extends ManKind{//继承ManKind父类 private int yearsOld; public void printAge() { System.out.println(this.yearsOld); } public static void main(String[] args) { Kids someKid = new Kids();//创建kids子类对象 someKid.salary=1;//调用父类属性 someKid.sex=1; someKid.employedd();//调用父类方法 someKid.manOrWorman(); } }
//Circle类 public class Circle { private double radius; public Circle() { radius = 1; } //设置半径 public void setRadius(double radius) { this.radius = radius; } //返回半径 public double getRadius() { return this.radius; } //计算圆面积公式 public double findArea() { return 3.14*radius*radius; } } //Cylinder类 public class Cylinder extends Circle {//圆柱继承圆 private double length;//高 public Cylinder() { length = 1; } public void setLength(double length) { this.length = length; } public double getLength() { return this.length; } //计算圆体积:底面积*高 public double findVolume() { return findArea()*getLength(); } } //TestCylinder类 public class TestCylinder { public static void main(String[] args) { Cylinder cylinder = new Cylinder(); cylinder.setRadius(3);//设置继承父类圆的半径 cylinder.setLength(3);//设置圆柱的高 System.out.println(cylinder.findVolume());//调用计算圆柱体积公式 } } //注意上面的三个类我放在一起了,如果复制在同一类中,只能保留其他一个public修饰符,其他public去掉
方法的重写
例子:
//person类 public class Person {//父类 private int age; private String name; private int sex; public void showInfo() { System.out.println("父类的方法"); System.out.println(this.age); System.out.println(this.name); System.out.println(this.sex); } public void setInfo(int age, String name , int sex) { System.out.println("父类的方法"); this.age = age; this.name = name; this.sex = sex; } } //student类 public class Student extends Person{ private String school; public void showInfo() { System.out.println("子类的方法,这里方法名一致,参数列表也是空的,返回值类型一致,算是重写了父类的方法"); System.out.println(this.school); } public void showInfo(String school) { System.out.println("这里改变了父类的参数列表,不算是重写父类的方法但是方法名和上面一致构成了方法重载"); System.out.println(school); } public void setInfo(String school) { System.out.println("子类的方法,这里改了父类的参数列表,不算方法的重写"); System.out.println(school); } public static void main(String[] args) { Student student = new Student(); student.showInfo();//调用了本类中的方法,因为父类的showInfo被重写了 student.showInfo("学校");//调用了本类的重载方法 student.setInfo(18, "猪头", 1);//这里调用的是父类的方法,虽然这个方法和本类的方法setInfo名字相同,但是参数列表不同,不算重写父类的方法 } }
注意:方法的重载和方法的重写区别,方法的重载:一个类可以有多个同名的方法。方法的重写:子类可以重写父类的方法,覆盖父类方法
练习:
重写父类的方法,只能修改方法体的内容,返回值、参数列表、方法名必须一致,而且修饰符的访问权限子类不能大于父类的,才算是重写父类的方法,这里违法了修饰符访问权限大于父类,不构成方法的重写。
注意:只要父类有private修饰,其他类就访问不了该类被private修饰的属性/方法,如果要重写父类的方法,父类不能是private修饰,这样子类就算想重写父类也做不到。
//Mankind类上面的1.1练习有 //Kids类 public class Kids extends ManKind{//继承ManKind父类 private int yearsOld; public void printAge() { System.out.println(this.yearsOld); } public void employed() { System.out.println("Kids should study and no job"); } public static void main(String[] args) { Kids someKid = new Kids();//创建kids子类对象 someKid.salary=1;//调用父类属性 someKid.sex=1; someKid.employed();//调用重写父类的方法,就是本类的方法 someKid.manOrWorman(); } }
权限修饰符
super关键字
例子:
//Person1类 public class Person1 { String name;//父类属性 int age; public Person1() {}//父类无参构造 public Person1(String name,int age) {//父类有参构造 this.name = name; this.age = age; } public void showMsg(String name,int age) {//父类的方法 System.out.print("姓名:"+name+" 年龄:"+age+" "); } } //Student1类 public class Student1 extends Person1 {//继承Person1父类 private String school; public Student1() {} public Student1(String name,int age,String school) {//子类构造 super(name,age);//调用父类的构造方法 this.school = school; } public void showMsg() { super.showMsg(this.name, this.age);//调用父类的方法,使用继承的属性 System.out.println("学校:"+this.school); } public static void main(String[] args) { Student1 student = new Student1("质数",19,"北大"); student.showMsg(); } }
练习:
public class Kids extends ManKind{//继承ManKind父类 private int yearsOld; public void printAge() { System.out.println(this.yearsOld); } public void employed() { super.employed();//调用父类方法 System.out.println("but Kids should study and no job"); } public static void main(String[] args) { Kids someKid = new Kids();//创建kids子类对象 someKid.salary=1;//调用父类属性 someKid.sex=1; someKid.employed();//调用重写父类的方法,就是本类的方法 someKid.manOrWorman(); } }
//Circle父类 public class Circle { private double radius; public Circle() { radius = 1; } //设置半径 public void setRadius(double radius) { this.radius = radius; } //返回半径 public double getRadius() { return this.radius; } //计算圆面积公式 public double findArea() { return 3.14*radius*radius; } } //Cylinder类 public class Cylinder extends Circle {//圆柱继承圆 private double length;//高 public Cylinder() { length = 1; } public void setLength(double length) { this.length = length; } public double getLength() { return this.length; } //覆盖计算圆面积公式,改为计算圆柱表面积: 2*π*半径*高+π*半径²*2 public double findArea() { return 2*(3.14*super.getRadius()*this.length)+(3.14*super.getRadius()*super.getRadius())*2; } //计算圆体积:底面积*高,底面积=(表面积-(2*π*半径*高))/2 public double findVolume() { return getLength()*((findArea()-(2*3.14*super.getRadius()*getLength()))/2); } } //TesCylinder测试类 public class TestCylinder { public static void main(String[] args) { Cylinder cylinder = new Cylinder(); cylinder.setRadius(3);//设置继承父类圆的半径 cylinder.setLength(3);//设置圆柱的高 System.out.println(cylinder.findArea());//计算圆柱表面积 System.out.println(cylinder.findVolume());//调用计算圆柱体积公式 Circle circle = new Circle(); circle.setRadius(3); System.out.println(circle.findArea());//计算父类圆的面积 } }
//TestFather类 public class TestFather { public int age; public int sex; public String name; public TestFather(int age,int sex,String name) { this.age = age; this.sex = sex; this.name = name; } public void show() { System.out.println("父类的this"); } public TestFather() { System.out.println("子类默认调用父类的无参构造"); } } //TestSon类 public class TestSon extends TestFather{ private String course; public TestSon(int age,int sex,String name,String course) { super(age, sex, name);//当父类没有无参构造器时,要指定父类的有参构造,否则报错,因为默认访问父类无参构造 this.course = course; } public TestSon() { //当子类构造器里面没有指定使用父类或本类构造器时 默认访问父类构造 //如果父类不存在无参构造 这里的无参构造器又没指定父类的有参构造或本类其他构造会报错 } public static void main(String[] args) { TestSon testSon = new TestSon();//这里会去访问父类的无参构造 } }
总结:父类只要存在无参构造,子类继承了必须要存在无参构造,父类存在有参构造,子类也必须存在有参构造,否则报错。this.属性/方法指定本类的属性/方法时,如果本类不存在,则寻找父类的(构造方法除外),super直接指定父类的属性/方法,多层继承也可访问到。
简单类对象实例化过程
public class TestSon{ private int age;//3属性默认初始化 ,基本类型默认初始为0,引用类型为null,默认初始完,到4构造方法进栈 static{ System.out.println("最先执行加载TestSon.class文件,再到静态,静态只执行1次"); System.out.println("1静态代码块"); } private int sex = 1;//5对属性进行显示初始化 public TestSon() {//4构造方法进栈,进栈之后不会先进入构造方法,而是5对属性进行显示初始化 System.out.println("6然后才进入无参构造"); }//这里构造方法弹栈,弹栈之后就进行7地址赋值给变量 public static void main(String[] args) { System.out.println("2进入main"); TestSon p = new TestSon();//先进栈为p分配空间,再到堆为new TestSon()分配空间,并在对象空间中,对对象的(3)属性进行默认初始化 System.out.println("7把new TestSon()对象的堆地址赋值给p变量"); } }
总结:
- 加载TestSon.class文件进内存
- 在栈内存为p开辟空间
- 在堆内存为TestSon对象开辟空间
- 对TestSon对象的成员变量进行默认初始化
- 构造方法进栈
- 对TestSon对象的成员变量进行显示初始化
- 进入构造方法
- 构造方法弹栈
- TestSon对象初始化完毕,把对象地址赋值给p变量
子类对象实例化过程
//TestFather类 public class TestFather { static{ System.out.println("最先执行加载父类和子类的class文件,再到静态,静态只执行1次"); System.out.println("1先执行父类静态代码块"); } private int age;//3先父类默认初始化 private int sex = 1;//6父类显示初始化属性 public TestFather() {//5父类的构造进栈,然后不会进入构造方法内,而是6父类显示初始化属性 System.out.println("父类显示初始化属性完后。--子类默认先调用父类的无参构造"); }//父类构造方法弹栈,到了7子类显示初始化属性 } //TestSon类 public class TestSon extends TestFather{ private int age;//3属性默认初始化 ,基本类型默认初始为0,引用类型为null,默认初始完,到4构造方法进栈 static{ System.out.println("2子类静态代码块"); } private int sex = 1;//7子类显示初始化属性,显示初始化完后,进入8构造方法 public TestSon() {//4子类构造方法先进栈,然后5父类的构造进栈 System.out.println("8然后才进入无参构造"); }//这里构造方法弹栈,弹栈之后就进行9地址赋值给变量 public static void main(String[] args) { System.out.println("3进入main"); TestSon p = new TestSon();//先进栈为p分配空间,再到堆为new TestSon()分配空间,并在对象空间中,对对象的(3)属性进行默认初始化(这里的属性默认初始化包括父类) System.out.println("9把new TestSon()对象的堆地址赋值给p变量"); } }
总结:
- 加载TestFather.class、TestSon.class文件进内存
- 在栈内存为p开辟空间
- 在堆内存为TestSon对象开辟空间
- 对TestSon对象的成员变量和父类的成员变量进行默认初始化
- 子类构造方法先进栈
- 父类构造方法后进栈
- 父类显示初始化属性
- 进入父类构造方法,父类构造弹栈
- 对TestSon对象的成员变量进行显示初始化
- 进入子类构造方法,子类构造弹栈
- TestSon对象初始化完毕,把对象地址赋值给p变量
多态
引用类型变量指向多种不同类型的对象的例子如下:
引用类型p可以指向Person类型,Student类型,其他对象类型。
多态虚拟方法的调用
例子:注意看注释
//Person类
public class Person {//父类
private int age;
private String name;
private int sex;
public Person() {}
public Person(int age,String name,int sex) {
this.age = age;
this.name = name;
this.sex = sex;
}
public void showInfo() {
System.out.println("父类的方法");
}
}
//Student类
public class Student extends Person{
private String school;
public Student() {
super(11,"猪头",1);
}
public void showInfo() {
System.out.println("子类的方法,虽然引用类型e是Person类,但是方法是运行时才调用的,所以调用的是Student类的方法");
}
public static void main(String[] args) {
Person e = new Student();
//多态属性调用: 父类引用类型e调用子类属性,会报错,e是编译期类型,也就是Person类型
e.school = "xxx";//这里报错,因为编译时e是Person类型的引用数据,不存在school属性
e.showInfo();//这里会调用Student对象的方法,不会调用Person类的方法,因为方法是运行时才调用
//注意多态调用子类方法时,父类需要存在和子类相同名字的方法,不然编译期报错。
}
}
多态小总结:
多态性向上转型
//Person类 public void method(Person p) { p.showInfo();//如果传了一个Student对象过来,刚好子类重写了showInfo这个方法,那么就会调用子类的showInfo,否则调用父类的 } public void showInfo() { System.out.println("父类的方法"); } } //Student类 public class Student extends Person{ public void showInfo() { System.out.println("子类的方法,虽然method的形参是Person类,但是方法是运行时才调用的,所以调用的是Student类的方法"); } public static void main(String[] args) { Person p = new Person(); Student s = new Student(); p.method(s);//method的形参是Person父类,但是传了一个Student子类过去形成多态,刚好子类重写了method里面的showInfo方法,方法是运行期类型,所以当执行时,会执行子类里面的showInfo方法 } }
多态性向下转型
//Person类 public class Person {//父类 public void work() { System.out.println("工作"); } public void hobby() { System.out.println("爱好"); } } //Student类 public class Student extends Person{ public void work() { System.out.println("学习"); } public void hobby() { System.out.println("打篮球"); } public void learn() { System.out.println("学生特有的学习方法"); } } //Teacher类 public class Teacher extends Person{ public void work() { System.out.println("教学"); } public void hobby() { System.out.println("读书"); } public void teach() { System.out.println("老师特有的教学方法"); } } //Test测试类 public class Test { //多态的向下转型需要强转,在引用变量前加上(强转的类型) //多态的向下转型好处是避免了写多个类型的goWork方法,比如 goWork(Student s),goWork(Teacher t)。利用多态的向下转型,一下搞定,减少了代码的重复率 public void goWork(Person p) { if(p instanceof Student) { //instanceof关键字判断 该引用变量 是否为该类型 //如果要使用多个子类特有的方法,可以先把p类型先强制为Student类型,p=(Student)p; p.work();//work不是学生类特有的方法,所以不需要向下转型,这里为向上转型,自动调用重写的work方法,因为方法是运行期类型,所以会调用子类的work方法 ((Student) p).learn();//learn为学生类特有的方法,父类Person没有这个方法,需要把形参Person类强转为Student类,才可以使用learn方法。 } else if(p instanceof Teacher) { p.work(); ((Teacher) p).teach();//teach也是Teacher子类特有的方法,想要编译期识别到teach方法,必须把p引用类型向下转型为Teacher类型。 } else { p.work(); } } public static void main(String[] args) { Test test = new Test(); Student s = new Student(); Teacher t = new Teacher(); test.goWork(t);//把教师类传过去,goWork方法的形参是Person父类,自动构成多态的向上转型。 test.goWork(s);//把学生类传过去,会使用学生类的 } }
Object类
Object顶级父类的方法应用例子:
public static void main(String[] args) { Person p = new Person(); Person p1 = new Person(); //Object类的equals方法,判断是否同一个对象,不是指同一类。 System.out.println(p.equals(p1));//false, p和p1是两个不同的对象,每次new都会在堆内存开辟空间,创建新的对象。p和p1都进行了new操作,所以p和p1都指向不同的内存地址。 p1 = p;//进行赋值操作 System.out.println(p.equals(p1));//true,上一条语句把p的内存地址赋值给p1,p1指向了p的内存地址,就是同一对象的意思。 //说明:为社么equals是Object类的方法,但是Person类的p引用变量可以使用呢,因为Java所有的类默认继承Object类,Object类是所有Java类的父类,所有子类可以使用父类Object的方法. Object object = new Object();//创建Object类就是调用Object的构造方法 System.out.println(object.hashCode());//输出object引用对象的哈希码 System.out.println(object.toString());//toString是当前引用对象的地址,就是堆地址。 //Object作为最顶级父类,可以使用多态接收子类的实例对象,比如: Object o = new Student(); Object oj = new Person(); //可以使用匿名对象使用Oject的方法 System.out.println(new Student().toString()); }
对象类型转换
数据类型转换例子:里面的引用类上面的练习有
public class Test { public static void main(String[] args) { //基本类型转换 int i = 10; long l = i;//小类型转成大类型,不用声明,自动转换 long a = 10l; int b = (int)a;//大类型转小类型需要声明,强制转换 //引用类型转换 Student s = new Student(); Person p = s;//从子类到父类的类型转换可以自动进行,向上转型 Person p1 = new Person(); Student s1 = (Student) p1;//从父类到子类的类型转换必须通过造型(强制类型转换)实现,向下转型 Test t = new Test(); Person p1 = t;//无继承关系的引用类型间的转换是非法的 Object objPri = new Integer(5);//int的包装类型 //所以下面代码运行时引发ClassCastException异常 String str = (String)objPri;//这里运行时会报异常 int b = new String();//基本类型不能和引用类型转换 //Object是所有类的最高父类 String s2 = "hello"; Object obj = s2;//从子类到父类的类型转换可以自动进行 System.out.println(obj); Object obj1 = "hello"; String s3 = (String) obj1;//从父类到子类的类型转换必须通过造型(强制类型转换)实现 System.out.println(s3); } }
equals和==的区别
例子:
public class Test1 { public static void main(String[] args) { //==操作符 //==基本类型比较:只要两个变量的值相等,即为true int i = 3; System.out.println(i == 3);//true //==引用类型比较:(是否指向同一个对象):只有指向同一个对象时,==才返回true //==比较的是所有的引用类型的地址,没有特殊情况 //注意用 “==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错 Person p1 = new Person(); Person p2 = new Person(); System.out.println(p1 == p2);//false,每次new操作都会产生新对象,分配堆内存地址给引用变量,p1和p2的引用变量里面存的内存地址不是同一个,即不是同一个对象 p1 = p2;//这里把p2引用的内存地址赋值给p1,这时p1和p2同时指向同一内存地址,就是同一对象 System.out.println(p1 == p2);//true,当为同一对象时,才会为true。==比较对象时,比较的是内存地址 //equals()方法 //equals方法只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。 //特殊:但是equals方法对于类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象 //原因:在这些类中重写了Object类的equals()方法。 String a = new String("a");String b = new String("a"); int c = new Integer("1"); String d = new String("1"); System.out.println(a.equals(c));//false , 虽然equals比较的是内容,但是引用类型不一致也会返回false System.out.println(a.equals(b));//true , equals对于特殊类比较的是内容 System.out.println(a == b);//false , == 比较的是所有引用类型的引用地址 //补充:如果你想改变某一个类的的equals方法,不想用equals比较地址,而是比较值,可以重写equals方法,注意记得加判断 是否为同一引用类型,再进行判断值,而且最好值放前面,如;"值".equals(变量),这种写法可以避免空指针 } }
总结:比较基本数据类型相等用”==“,判断是否同一对象时最好也用”==“,虽然equals也可以判断同一对象,但是equals对File、String、Date和包装类是判断值的。
补充:如果你想改变某一个类的的equals方法,不想用equals比较地址,而是比较值,可以重写equals方法,注意记得加判断 是否为同一引用类型,再进行判断值,而且最好值放前面,如:"值".equals(变量),这种写法可以避免空指针。
String对象的创建
例子:
public class Test2 { public static void main(String[] args) { //字面值创建String对象 String a1 = "a"; String a2 = "a"; String a3 = "a" + "b"; // 这里是直接在常量池中创建 "ab"对象,并返回常量池地址 String a4 = "a" + "b"; //字面值创建String对象,只会在字符串常量池中创建String对象,常量池在堆空间中 //流程:首先会在常量池中判断是否存在字符串a对象,没有则创建,在常量池创建a对象后,返回a对象的常量池地址给a1 //然后a2创建对象时,检测到常量池已经存在a对象,返回a对象的常量池地址给a2存储,这时a1和a2都指向a对象的常量池地址 System.out.println(a1 == a2);//true,==判断常量池的地址 System.out.println(a3 == a4);//true //new对象创建String对象 String b1 = new String("b"); String b2 = new String("b"); //new对象创建String对象,会先在常量池中创建String对象,然后在堆空间再创建一个String对象,并返回的是堆空间的地址。 //流程:首先会在常量池中判断是否存在字符串b对象,没有则创建,在常量池创建b对象后,然后在堆空间再次创建b对象,并返回b对象的堆内存地址给b1 //然后b2创建对象时,检测到常量池已经存在b对象,则不在常量池创建b对象,只在堆空间创建b对象,返回堆空间的b对象的地址。 System.out.println(b1 == b2);//false //new创建字符串拼接对象:是通过StringBulider实现的 //流程:首先在字符串常量池中创建一个“1”对象,然后想再次在字符串常量池创建“1”对象时发现已经存在了“1”对象,两个字符串对象拼接完成后内容变成“11”,接着在堆空间创建“11”对象,并且返回“11”对象的地址给s3 String s3 = new String("1") + new String("1"); String s4 = new String("11"); System.out.println(s3 == s4);//false } }
注意:字面值创建String拼接对象只会在常量池中创建一个拼接对象,不会分别创建多个对象。
new String 创建String拼接对象,先去常量池分别创建多个对象,如果在常量池已经存在,则不创建,在常量池中创建完对象后,再进行拼接对象,最后在堆中只创建一个拼接后的对象。最后强调一下:==是比较内存地址,equals对于String是比较值。
练习
public class Test3 { public static void main(String[] args) { int it = 65; float fl = 65.0f; System.out.println("65和65.0f是否相等?" + (it == fl)); //true ,基本类型判断不区分类型,只比较值 char ch1 = 'A'; char ch2 = 12; System.out.println("65和‘A’是否相等?" + (it == ch1));//true,65是A的ASCII码 System.out.println("12和ch2是否相等?" + (12 == ch2));//true String str1 = new String("hello"); String str2 = new String("hello"); System.out.println("str1和str2是否相等?"+ (str1 == str2));//false,不是同一个对象 System.out.println("str1是否equals str2?"+(str1.equals(str2)));//true,特殊情况,在String中equals是用来比较值的 // System.out.println("hello" == new java.sql.Date()); //编译不通过 //对象类型里面的值比较 Person p1 = new Person(); p1.name = "atguigu";//这里是对象里面的String类型赋值,这是字面值创建String,只在常量池中创建对象 Person p2 = new Person(); p2.name = "atguigu";//这里去字符串常量池创建对象,发现已存在,直接返回存在的对象的地址 System.out.println(p1.name .equals( p2.name));//true,name属性是字符串 System.out.println(p1.name == p2.name);//true,虽然两个person对象地址不一样,但是里面的String类型的值都是指向同一个常量池地址 System.out.println(p1.name == "atguigu");//true,这里比较的是常量池地址 String s1 = new String("bcde"); String s2 = new String("bcde"); System.out.println(s1==s2);//false,比较的是地址 } }
public class Order { private int orderId; private String OrderName; public Order() {} public Order(int orderId,String OrderName) { this.orderId = orderId; this.OrderName = OrderName; } public int getOrderId() { return orderId; } public void setOrderId(int orderId) { this.orderId = orderId; } public String getOrderName() { return OrderName; } public void setOrderName(String orderName) { OrderName = orderName; } //重写equals方法判断对象之前是否属性的值全一致 public boolean equals(Object obj) { boolean flag = false; //判断对象是否为同一类型 if(obj instanceof Order) { Order o = (Order)obj;//强制为Order类型 //判断id/name里面的值是否一致 if(o.getOrderId() == this.getOrderId() && o.getOrderName().equals(this.getOrderName())) { flag = true; } } return flag; } public static void main(String[] args) { Order o1 = new Order(111,"a"); Order o2 = new Order(111,"a"); System.out.println(o1.equals(o2));//true,里面的值一致 } }
2.请根据以下代码自行定义能满足需要的MyDate类,在MyDate类中覆盖equals方法,使其判断当两个MyDate类型对象的年月日都相同时,结果为true,否则为false。 public boolean equals(Object o)
public class MyDate { private int year; private int month; private int day; public MyDate() {} public MyDate(int year,int month,int day) { this.year = year; this.month = month; this.day = day; } @Override public boolean equals(Object obj) { boolean flag = false; if(obj instanceof MyDate) { MyDate date = (MyDate)obj; if(date.year == this.year && date.month == this.month && date.day == this.day) { flag = true; } } return flag; } public static void main(String[] args) { MyDate d1 = new MyDate(2019,12,12); MyDate d2 = new MyDate(2019,12,12); System.out.println(d1.equals(d2));//true,比较的是值,如果值一致则代表一样 } }
toString()方法
例子
MyDate ss = new MyDate(2018,12,12); // 创建对象时,打印引用对象的值,都是内存地址,如果想打印对象的信息,就需要重写toString()方法 /** * 重写MyDate类的toString()方法 * @Override public String toString() { return "MyDate [year=" + year + ", month=" + month + ", day=" + day + "]"; } */ System.out.println(ss);//org.chen.day08.MyDate@54bedef2,重写toString()方法后,打印出MyDate [year=2018, month=12, day=12]
包装类
基本数据类型包装成包装类的实例 ---装箱
获得包装类对象中包装的基本类型变量 ---拆箱
例子:
//装箱 Boolean b = new Boolean(false);//boolean类的包装类 Integer i = new Integer(12);//int类的包装类 Long lo = new Long(33);//long类的包装类 Character ct = new Character('a');//char类的包装类 Float f = new Float(11);//float类的包装类 Double d = new Double(55);//double类的包装类 //注意:除了char和int的包装类区别明显,其他都是首字母大写,所以有可能不小心使用到包装类,最常见就是Boolean和boolean用错 //拆箱,拆箱方法:包装类.基本数据类型Value(),如下所示 boolean b1 = b.booleanValue(); int i1 = i.intValue(); double db1 = d.doubleValue(); //jdk1.5版本后,已经支持自动装箱/拆箱了,前提是基本数据类型要和包装类型一致,如:int和Integer //自动装箱 //由于支持了自动装箱,所以有时候想用基本数据类型,但是错用包装类,但是不会报错,因为是属于自动装箱 Boolean b2 = false;//注意这里是boolean的包装类 Integer in = 32;//自动装箱 //自动拆箱 boolean bz = b2;//这里不需要用到手动的拆箱方法了 int in2 = in;//自动拆箱,存放到基本数据类型中
字符串转换位基本数据类型
//把字符串转为基本数据类型 //1、通过包装类把字符串转为int包装类型,然后拆箱赋值给基本数据类型int int cc = new Integer("1234"); float ff = new Float("322.9"); boolean bls = new Boolean("true"); System.out.println(bls); //2、通过包装类的parseXXX()方法把里面的字符串转为基本数据类型 int ccs = Integer.parseInt("232"); boolean bsl = Boolean.parseBoolean("false");
基本数据类型转换为字符串
//基本数据类型转字符串 //1、通过String.valueOf()方法把基本数据类型转为字符串,所有基本数据类型都可以 String booleans = String.valueOf(true); String ints = String.valueOf(123); String doubles = String.valueOf(32.2); //2、字符串拼接直接变成字符串,注意只有+字符串的时候才算是字符串拼接,如果前面都是数字相加+就会运算结果,遇到+“”才会开始拼接字符串,也就是转换为字符串 String te = 23 + ""; String ds = 32.1 + ""; String we = true + ""; System.out.println(we);//这里输出的true只是字符串,不是boolean的true