• 8java继承、方法重写、权限修饰符、super关键字、对象实例化过程、多态、Object类、包装类


    继承

    如果让你创建学生类你可能一下就能创建完,但是如果让你创建学生类,工人类,教师类,农民类,而且这些类的属性都有相同的属性和方法:姓名,年龄,性别,打印个人信息的方法。按照前面说的把代码写出来,如果直接这样去硬写,是不是代码的重复就很多。

    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());//这里也是调用父类的方法打印名字
        }
    }
    View Code

     

     

    不要为了获取其他类中的某些属性/方法/功能而去继承。比如创建一个狗类,狗也有姓名,年龄,性别。但是狗类可以继承人类吗,不可以。虽然语法上不会报错,但是思想上不符合逻辑。如果狗类想继承,可以创建一个动物类,然后狗类继承动物类。

    继承需要逻辑关系在里面,不要随意继承。

     

     

     

     练习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();
        }
    }
    View Code

    //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去掉
    View Code

    方法的重写

     例子:

    //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名字相同,但是参数列表不同,不算重写父类的方法
        }
    }
    View Code

     注意:方法的重载和方法的重写区别,方法的重载:一个类可以有多个同名的方法。方法的重写:子类可以重写父类的方法,覆盖父类方法

    练习:

     重写父类的方法,只能修改方法体的内容,返回值、参数列表、方法名必须一致,而且修饰符的访问权限子类不能大于父类的,才算是重写父类的方法,这里违法了修饰符访问权限大于父类,不构成方法的重写。

    注意:只要父类有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();
        }
    }
    View Code

    权限修饰符

     

    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();
        }
    }
    View Code

    练习:

    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();
        }
    }
    View Code

    //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());//计算父类圆的面积
        }
    }
    View Code

     

    //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();//这里会去访问父类的无参构造
        }
    }
    View Code

    总结:父类只要存在无参构造,子类继承了必须要存在无参构造,父类存在有参构造,子类也必须存在有参构造,否则报错。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变量");
        }
    }
    View Code

    总结:

    1. 加载TestSon.class文件进内存
    2. 在栈内存为p开辟空间
    3. 在堆内存为TestSon对象开辟空间
    4. 对TestSon对象的成员变量进行默认初始化
    5. 构造方法进栈
    6. 对TestSon对象的成员变量进行显示初始化
    7. 进入构造方法
    8. 构造方法弹栈
    9. 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变量");
        }
    }
    View Code

    总结:

    1. 加载TestFather.class、TestSon.class文件进内存
    2. 在栈内存为p开辟空间
    3. 在堆内存为TestSon对象开辟空间
    4. 对TestSon对象的成员变量和父类的成员变量进行默认初始化
    5. 子类构造方法先进栈
    6. 父类构造方法后进栈
    7. 父类显示初始化属性
    8. 进入父类构造方法,父类构造弹栈
    9. 对TestSon对象的成员变量进行显示初始化
    10. 进入子类构造方法,子类构造弹栈
    11. 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类的方法,因为方法是运行时才调用
            //注意多态调用子类方法时,父类需要存在和子类相同名字的方法,不然编译期报错。
        }
    }
    View Code

    多态小总结:

     

     多态性向上转型

    //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方法
        }
    }
    View Code

    多态性向下转型

    //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);//把学生类传过去,会使用学生类的
        }
    }
    View Code

    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());
        }
    View Code

    对象类型转换

     数据类型转换例子:里面的引用类上面的练习有

    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);
        }
    }
    View Code

     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(变量),这种写法可以避免空指针    
        }
    }
    View Code

    总结:比较基本数据类型相等用”==“,判断是否同一对象时最好也用”==“,虽然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
    
        }
    }
    View Code

    注意:字面值创建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,比较的是地址
    
        }
    }
    View Code

    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,里面的值一致
        }
    }
    View Code

     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,比较的是值,如果值一致则代表一样
        }
    }
    View Code

    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]
    View Code

    包装类

     

    基本数据类型包装成包装类的实例    ---装箱

     

     获得包装类对象中包装的基本类型变量    ---拆箱

     例子:

        //装箱
            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;//自动拆箱,存放到基本数据类型中
    View Code

    字符串转换位基本数据类型

    //把字符串转为基本数据类型
            //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");
    View Code

    基本数据类型转换为字符串

    //基本数据类型转字符串
            //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
    View Code
  • 相关阅读:
    关于AE大数据点文件读取生成SHP文件时使用IFeatureBuffer快速提高读取效率
    随手写了个opengl的demo
    render Target sample in UI
    堆排序
    PAT 1040. Longest Symmetric String
    为什么要学习机器学习?如何学习
    输出一个字符串的全排列
    关于Logistic Regression的疑问
    PAT上机考试模板
    如何设置tomcat的默认访问路径
  • 原文地址:https://www.cnblogs.com/unlasting/p/12393979.html
Copyright © 2020-2023  润新知