• Practice| 面向对象


    实参与形参的传递机制

    * 实参给形参赋值:
    * 1、基本数据类型:
    * 实参给形参的数据值,形参的修改和实参无关
    * 2、引用数据类型
    * 实参给形参的地址值,如果这个地址值修改“属性”会影响实参,但是你如果修改是地址值,和实参无关了
    */

    class MianShiTi{
        String str = new String("good idear"); //创建字符串对象;
        char[] ch = {'a','b','c','d'}; //创建数组;
        
        public static void main(String[] args){
            MianShiTi m = new MianShiTi(); //创建一个对象;
            
            exchange(m.str, m.ch);
            //m.str没有变,m.ch的[1]变了
            System.out.println(m.str); //good idear ,它们是两个无关的str
            System.out.println(m.ch); //a*cd
        
        
        
            String s = "bbbb";
            StringBuffer b = new StringBuffer("bbbb");
            change(s,b);
            //s不可变,s仍然"bbbb"
            //b的内容就从"bbbb" ---> "aaaa"
            System.out.println(s+b); //bbbbaaaa
        
        }
    
        public static void exchange(String str, char[] ch){ //两个都是引用数据类型String和char[]数组,
            str = "Hello World"; //字符串对象是不可变的,一旦修改str=xxx,就是产生新的对象,和原来的"实参"无关
            System.out.println(str); //Hello World
            ch[1] = '*';  //数组元素是可变的,ch形参和上面的m.ch实参指向的是同一个数组,所以ch[1]修改,就是修改m.ch[1]
            System.out.println(ch); //a*cd
        }
        
        
        private static void change(String s, StringBuffer b){  //两个形参为String类型和 类 都是引用数据类型
            s = "aaa";
            b.setLength(0);//b的长度设置为0,相当于清空原来b中内容
            b.append("aaaa");//append表示追加,又追加了aaaa
        }
        
        
        
    }
    public class MainTest{
        public static void test(String str){
            str = "hello";
        }
        public static void main(String[] args){
            String str = "beijing"; 
            test(str);
            System.out.println(str);  //beijing
        }
    }
    public class TestMethod {
    
        //i,str,j是形参
            public static void change(int i, String str, Integer j, MyData my){
                i++;//这里i无论怎么改,和实参无关
                str += "尚硅谷";//字符串对象是不可变的,一旦改变,会产生新对象,str中的地址值变了
                j++;//Integer对象是不可变的,一旦改变,会产生新对象,j中的地址值变了
                //my.num = 20;
                my = new MyData();
                my.num = 100;
            }
            public static void main(String[] args){
                int a = 10;
                String s = "hello";
                Integer b = 20;
                MyData m = new MyData();
                
                change(a,s,b,m);//a,s,b,m是实参
                
                System.out.println("a = " + a); //a=10
                System.out.println("s = " + s); //s=hello
                System.out.println("b = " + b); //b=20
                System.out.println("m.num = " + m.num); //n.num=100
        }
    }
    class MyData{
        int num = 10;
    }
    View Code
    class TEXT{
        public int num;
        public String str;
        
        public TEXT(int num, String str){
            this.num = num;
            this.str = str;
        }
    }
    public class Class4 {
        public static void f1(TEXT tIn, int intIn, Integer integerIn, String strIn){
            tIn.num =200;
            tIn.str = "bcd";
            intIn = 200;
            integerIn = 200;
            strIn = "bcd";
        }
        public static void main(String[] args) {
            TEXT tIn = new TEXT(100, "abc");  //tIn引用数据类型,对象
            int intIn = 100; 
            Integer integerIn = 100; 
            String strIn = "abc";
            
            f1(tIn,intIn,integerIn,strIn);
                                //引用数据类型 + 引用数据类型+int+Integer +String
            System.out.println(tIn.num + tIn.str + intIn + integerIn + strIn);
        }                       //200 bcd + 100 + 100 + abc
    }
    
    ===>>>200bcd100100abc
    View Code

     

    用数组来管理属性、对象等

    /*
    (1)声明一个日期类型MyDate:有属性:年year,月month,日day
    (2)创建2个日期对象,分别赋值为:你的出生日期,你对象的出生日期,放到数组中
    (3)遍历显示
    */
    
    public class TestMydate {
    
        public static void main(String[] args) {
            MyDate[] arr = new MyDate[2]; //创建长度为2的数组
            
            arr[0] = new MyDate();  //引用数据类型需要给它赋值一个对象;
            
            arr[0].year = 1994;
            arr[0].month = 7;
            arr[0].day = 7;
            
            arr[1] = new MyDate();
            arr[1].year = 1996;
            arr[1].month = 7 ;
            arr[1].day = 7;
            
            for(MyDate num : arr){
                System.out.println(num.year + "-" + num.month + "-" + num.day);
            }
            
    View Code
    class TestTeacher{
        public static void main(String[] args){
    
            //用数组来管理属性,创建的对象,排序等
            Teacher[] arr = new Teacher[5];  //声明一个一维数组,来存储5个Teacher的对象; 
            arr[0] = new Teacher();         //arr[0]代表一个元素,是Teacher类型(引用数据类型),需要赋值为一个Teacher对象;
            arr[0].name = "kris";
            arr[0].age = 18;
            arr[0].gender = '男';
            arr[0].course = "java";
            
            Teacher t2 = new Teacher();
            t2.name = "alex";
            t2.age = 11;
            t2.gender = '女';
            t2.course = "Mysql";
            arr[1] = t2;
            
            for(int i = 0;i < arr.length;i++){
                //arr[i]是一个Teacher的对象
                //Exception in thread "main" java.lang.NullPointerException空指针异常
                //一旦报NullPointerException,说明对象是null
                //arr[i]是否是null?
                //if(arr[i] != null){
                    System.out.println(arr[i].name + "	" + arr[i].age + "	" + arr[i].gender + "	" +arr[i].course);
                //}
                
            }
            
        }
    }
    
    class Teacher{
        String name;
        int age;
        char gender;
        String course;
    }
    练习2
    声明一个圆类Circle,有属性半径radius
    声明一个测试类TestCircleArray,
    在main方法中,创建一个Circle数组,长度为3,并创建三个Circle对象放到数组中,
    遍历显示数组,显示每一个圆的半径、面积。
    然后在对Circle数组进行排序,按照半径从小到大,再次遍历显示数组,显示每一个圆的半径、面积
    */
    
    class TestCircle2{
        public static void main(String[] args){
            
            Circle[] arr = new Circle[3]; //创建一个Circle数组,长度为3,默认值都为null
            
            //创建3个Circle对象放到数组中,   动态输入创建
            java.util.Scanner input = new java.util.Scanner(System.in);
            for(int i = 0; i < arr.length;i++){
                arr[i] = new Circle(); //先创建圆Circle的对象
                System.out.print("请输入圆的半径:");
                arr[i].radius = input.nextDouble();
                System.out.println("半径" + arr[i].radius + "	" + "面积" + Math.PI * arr[i].radius * arr[i].radius);
            }
            /* //静态输入
            arr[0] = new Circle();
            arr[1] =  new Circle();
            arr[2] =  new Circle();
            
            arr[0].radius = 2.0;
            arr[1].radius = 3.0;
            arr[2].radius = 1.0;
            */
            //冒泡排序按从小到大顺序排,需要排arr.length-1次,大的就往后
            //第一轮:i=1,j=0,1两次
            //第二轮:i=2,j=1一次
            for(int i = 1; i < arr.length;i++){
                for(int j = 0;j < arr.length-i;j++){ //如果前面的圆的半径比较后面的圆的半径大,就交换两个圆
                    if(arr[j].radius > arr[j+1].radius){  //比较的是圆的半径,对象需要.半径属性
                        Circle temp = arr[j];//temp的类型和arr[j]和arr[j+1]一样,它俩是Circle
                        arr[j] = arr[j+1];
                        arr[j+1] = temp;
                    }
                }
                
            }
            
            for(int i = 0; i < arr.length;i++){
                System.out.println(arr[i].radius);
            }
        }
    
    }
    class Circle{
        double radius;
    
    }
    练习3
    (1)声明一个丈夫类型Husband,有属性:姓名,年龄,妻子,其中妻子是Wife类型
    (2)声明一个妻子类型Wife,有属性:姓名,年龄,丈夫,其中丈夫是Husband类型
    (3)创建一个丈夫对象,创建一个妻子对象,并显示他们的信息
    
    */
    class Test3{
        public static void main(String[] args){
            
            Husband hus = new Husband(); //hus丈夫对象;
            hus.name = "kris"; 
            hus.age = 20; 
            
            Wife wif = new Wife();         //wif妻子对象;
            wif.name = "xka";
            wif.age = 19;
            
            hus.qi = wif;   //qi为引用数据类型,必须给它赋值一个对象;
            wif.fu = hus;
            
            System.out.println("丈夫的信息:"+hus.name + "	" + hus.age + "	" + "妻子的信息:" + hus.qi.name + hus.qi.age);
            System.out.println("妻子的信息:" + wif.name + "	" + wif.age + "	" +"丈夫的信息:"+ wif.fu.name);
            
        }
    }
    
    
    class Husband{
        String name;
        int age;
        Wife qi;
    
    }
    
    class Wife{
        String name;
        int age;
        Husband fu;
    
    }
    /*
    2、练习2
    (1)声明一个日期类型MyDate,包含年、月、日
    (2)声明一个员工类,包含属性:姓名、薪资、生日,其中生日是MyDate类型
    (3)声明一个测试类,在main方法中,声明一个数组,创建三个员工对象,并为属性赋值
    (4)遍历显示员工信息,并且按照生日排序
    (5)遍历显示员工信息,按照薪资排序
    */
    
    class Test2{
        public static void main(String[] args){
            Staff[] arr = new Staff[3]; //声明一个数组,创建员工对象,并为他们赋值;
            
            java.util.Scanner input = new java.util.Scanner(System.in);
            
            for(int i = 0; i < arr.length; i++){
                arr[i] = new Staff();
                System.out.print("请输入员工姓名:");
                arr[i].name = input.next();
                System.out.print("员工薪资:");
                arr[i].salary = input.nextInt();
                System.out.println("请输入员工生日");
                arr[i].birthday = new MyDate();  //birthday为引用数据类型,必须给它赋值一个对象;
                System.out.print("年:");
                arr[i].birthday.year = input.nextInt();
                System.out.print("月:");
                arr[i].birthday.month = input.nextInt();
                System.out.print("日:");
                arr[i].birthday.day = input.nextInt();
            }
            System.out.println("姓名	" + "薪资	" + "生	日");
            //遍历显示员工信息;foreach循环
            //for(元素的数据类型 元素名 : 数组名){}
            for(Staff num : arr){
                //System.out.println(arr[i].name + "	" + arr[i].salary + "	" + arr[i].birthday.year + "-" + arr[i].birthday.month + "-" + arr[i].birthday.day);
                System.out.println(num.name + "	" + num.salary + "	" + num.birthday.year + "-" + num.birthday.month + "-" + num.birthday.day);
            }
            
            
            
            //冒泡排序;按员工生日排序;从小到大
            for (int j = 1;j < arr.length;j++){
                
                for(int i = 0;i < arr.length-i; i++){
                    
                    if(arr[i].birthday.year > arr[i+1].birthday.year){ //先比较年year,大的年龄小
                    Staff temp = arr[i];
                    arr[i] = arr[i+1];
                    arr[i+1] = temp;
                }else if(arr[i].birthday.year == arr[i+1].birthday.year){
                    if(arr[i].birthday.month > arr[i+1].birthday.month){
                        Staff temp = arr[i];
                        arr[i] = arr[i+1];
                        arr[i+1] = temp;
                    }else if(arr[i].birthday.month == arr[i+1].birthday.month){
                        if(arr[i].birthday.day > arr[i+1].birthday.day){
                        Staff temp = arr[i];
                        arr[i] = arr[i+1];
                        arr[i+1] = temp;
                            }
                        }
                    }
                }
            }
            //遍历排序之后的
            System.out.println("排序之后的:");
            System.out.println("姓名	" + "薪资	" + "生	日");
            for(int i = 0;i < arr.length; i++){
                
                System.out.println(arr[i].name + "	" + arr[i].salary + "	" + arr[i].birthday.year + "-" + arr[i].birthday.month + "-" + arr[i].birthday.day);
            }
        }
    
    }
    class MyDate{
        int year;
        int month;
        int day;
        
    }
    class Staff{
        String name;
        double salary;
        MyDate birthday;
        
    }

    类的继承,super
    
    1、写一个名为Account的类模拟账户。该类的属性和方法如下图所示。该类包括的属性:账号id,余额balance,年利率annualInterestRate;
    包含的方法:访问器方法(getter和setter方法),返回月利率的方法getMonthlyInterest(),取款方法withdraw(),存款方法deposit()。 写一个测试类TestAccount:在用户程序中,创建一个账号为11223344、余额为20000、年利率4.
    5%的Account对象。使用withdraw方法提款30000元,并打印余额。 再使用withdraw方法提款2500元,使用deposit方法存款3000元,然后打印余额和月利率。 2、创建Account类的一个子类CheckAccount代表可透支的账户,该账户中定义一个属性overdraft代表可透支限额,。在CheckAccount类中重写withdraw方法。 写一个用户程序测试CheckAccount类。在用户程序中,创建一个账号为11223344、余额为20000、年利率4.5%,可透支限额为5000元的CheckAccount对象。 提示: (1)子类CheckAccount的构造方法需要将从父类继承的3个属性和子类自己的属性全部初始化。 (2)父类Account的属性balance被设置为private,但在子类CheckAccount的withdraw方法中需要修改它的值 最后思考:如果将父类Account的属性balance设置为protected,会怎么样?
    package com.atguigu.variable;
    
    import com.atguigu.inherited.Account;
    
    public class CheckAccount extends Account {
        
        private double overdraft;
    
        public CheckAccount() {
            super();
        }
    
        public CheckAccount(String id, double balance, double annualInterestRate, double overdraft) {
            super(id, balance, annualInterestRate);
            this.overdraft = overdraft;
        }
    
        public double getOverdraft() {
            return overdraft;
        }
    
        public void setOverdraft(double overdraft) {
            this.overdraft = overdraft;
        }
    
        
    /*    public void withdraw(double money){
            if(money < 0){
            System.out.println("输入有误");
            }if(money < getBalance()){ 
                super.withdraw(money); //正常取,可以使用父类里边的方法withdraw
            }else{  //可以透支
                if(money > getBalance() + overdraft){
                    System.out.println("超过可透支额度");
                }else{
                    System.out.println("剩余可透支额度为:" + (overdraft -= money - getBalance())); //取后剩余的透支额度;
                    //超过的额度(money - balance);
                    setBalance(0);
                    System.out.println("哎呀取完了,账户余额为:" + getBalance());
                }
                
            }
        }*/
        
    //    如果将父类Account的属性balance设置为protected,表在其他包的子类里边也可以使用
        public void withdraw(double money){
            if(money < 0){
                System.out.println("您的输入有误");
            }if(money < balance){
                super.withdraw(money);  
            }else{
                if(money > balance + overdraft){
                    System.out.println("超过卡的取款额度");
                }else{
                    System.out.println("可透支额度剩余:" + (overdraft -= money -balance));
                    setBalance(0);
                }
            }
        }
        
        
    }
    package com.atguigu.variable;
    
    import com.atguigu.inherited.Account;
    
    public class TestAccount {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Account a = new Account("11223344", 20000, 0.045);
            CheckAccount c = new CheckAccount("11223344", 20000, 0.056, 5000);
            
            c.withdraw(28000);//传参调用withdraw方法,它是没有返回值的。
            //System.out.println(a.getBalance());
            System.out.println(a.deposit(2000));
        
            
        }
    
    }

     多态转型

    // A(show(D obj) show(A obj)) --> B(show(B obj) show(A obj) ) --> C 
    //                                                                   --> D
    //父类的变量可以存储子类的对象;但子类的变量不能存储父类的对象;   D13Exam
    public class Exam1 {
        public static void main(String[] args) {
            A a1 = new A(); 
            A a2 = new B(); //向上转型 a2对象可以调用B中重写父类的方法show(A obj)(重写的形参看是否符合 父类的引用变量接收子类的对象。) 和 父类中的方法。
            B b = new B(); //本态引用,b可以调用它自己即子类B中的方法和继承父类的方法,只要参数符合就可以。
            C c = new C();
            D d = new D();
            System.out.println("(1)" + a1.show(b));//调用A类中的哪个show方法,实参是子类B的对象b,b可以传给它的父类引用变量 A obj 多态参数
        
            System.out.println("(2)" + a2.show(b));//B类的对象有几个show方法? D继承了B,B继承了A; 
    //((1)show(D obj)(2)show(A obj)(3)show(B obj)),这里传的实参类型D类型d对象,选择show(D obj)执行
    System.out.println("(3)" + b.show(c));//b类的对象有几个方法? b可以调用B和A中所有的方法,形参c 可以传给它的父类;多态参数。 // 这里传的实参类型是C类型的c对象,如果有形参C类型的,肯定首选,这里没有,看哪个合适,选择B类型 System.out.println("(4)" + b.show(d)); // 这里传的实参类型是D类型的d对象,有合适的, } } class A{ public String show(D obj){ return ("A and D"); } public String show(A obj){ //a1对象可以调用哪个传入B类型的show方法呢?B继承了A return "A and A"; } } class B extends A{ public String show(B obj){ return "B and B"; } @Override public String show(A obj){ //重写了父类show(A obj)的方法 return "B and A"; } } class C extends B{ } class D extends B{ ---》》 (1)A and A (2)B and A (3)B and B (4)A and D

    多态针对方法的,属性没有多态,注意权限修饰符

    public class Exam2 {
        public static void main(String[] args) {
            Base b = new Sub();//多态引用
            System.out.println(b.x);//属性没有多态,按照编译时,b对象有1个x,是Base类中 //1
        }
    }
    class Base{
        int x = 1;
    }
    class Sub extends Base{
        int x = 2;
    }
    View Code
    public class Child extends Father{
        public String grade;
        
        public static void main(String[] args){
            Father f = new Child();  //向上转型;
            System.out.println(f.name);//因为name是private的,所以编译错误;
        }
    }
    
    class Father{
        private String name = "atguigu";  //private 只能在本类中使用。
        int age = 0;
    }

     this、继承时构造器的调用

    public class Teacher extends Person{
        private String name = "tom";
        public Teacher(){
            System.out.println("this is a teacher.");
    //        super(); //编译错误,必须在子类构造器的首行
        }
        public static void main(String[] args){
            Teacher tea = new Teacher();
    //        System.out.println(this.name);//编译错误,this不能在static方法中使用的
        }
    }
    class Person{
        public Person(){
            System.out.println("this is a Person.");
        }
    }

    继承、构造器、super、方法的重写、多态引用

    public class Test {
    
        public static void main(String[] args) {
            Base b1 = new Base();//本态引用,只看Base类,执行了Base类的无参构造,在无参构造中,调用了Base类的method(100)
            Base b2 = new Sub();  //执行的是子类重写的方法。
            //多态引用,创建的是子类的对象,执行子类Sub的无参构造;
    //在子类的构造器中,一定会调用父类的构造器,默认调用的是父类的无参构造,会导致父类的无参构造被执行,因为父类的无参构造中调用method(100),
    //它省略了“this.”,这个this是当前对象,当前正在创建的是子类Sub的对象,执行的是子类Sub重写的method(100) 
    //接着在子类的构造器中,有super.method(70),这个执行的是父类的method(70),所以会打印 Base:70
        
        }
    }
    class Base{
        Base(){
            method(100); //省略了this.method(100),this指当前对象
        }
        public void method(int i){
            System.out.println("base : " + i);  // 1.base: 100;  3. base: 70
        }
    }
    class Sub extends Base{
        Sub(){
                  //省略了super();
            super.method(70);  //super.方法。子类重写了method方法,但是它又想调用父类的method方法了。
        }
        public void method(int j){  //子类重写了method方法。
            System.out.println("sub : " + j); //2.  sub: 100 ;
        }
    }
    
    --->>
    base : 100
    sub : 100
    base : 70

     类初始化和实例初始化

    如果没有实例化(例如main方法中执行了某个函数),没有创建对象,只执行类的实例化,不进行实例的初始化。

    public class Test {
        {
            System.out.println("b");
        }
        static{
            System.out.println("a");
        }
        Test(){
            System.out.println("c");
        }
        public static String getOut(){
            try {
                return "1";
            } catch (Exception e) {
                return "2";
            }finally{
                return "3";
            }
        }
        public static void main(String[] args) {
            System.out.println(getOut());
        }
    }
    
    ===》》 a  3
    View Code
    * 类初始化和实例初始化
    * (1)先类初始化
    *   A:如果父类没有初始化,会先初始化父类
    *   B:类的初始化执行的是<clinit>方法,它由两部分组成:
    *     (a)静态变量的显式赋值语句  (b)静态代码块的语句,(a)(b)谁在上谁先执行
    * 
    * (2)实例初始化
    *   A:创建子类对象时,也会导致父类的实例初始化方法执行
    *   B:每一个构造器都会有对应的实例初始化方法
    *   C:每一个实例初始化有三部分组成:
    *     (a)非静态变量的显示初始化代码(b)非静态代码块的语句(c)构造器;  (a)(b)谁在上谁先执行,构造器后执行

     子类继承父类,子类和父类都有无参构造和有参构造;子类在进行创建对象时,不管子类创建对象时候它有没有传参,

    子类都是默认调用父类的无参构造,如果传参了,那么参数传给的是子类的有参构造。

    练习:

    /*
     * 实例初始化的过程:
     * 1、父类的实例初始化<init>()
     *      因为子类默认调用父类的无参构造,那么选择父类的<init>(),而不是<init>(String name)
     *         System.out.print("1");
     * 2、子类的实例初始化
     *         System.out.print("3");
     *         father = new People(name +" F"); //创建了父类的对象
     *             这句代码会导致,父类的有参构造,即父类的<init>(String name)会执行
     *                 System.out.print("2");
     *         
     */
    public class TestChild {
        public static void main(String[] args) {
            new Child("mike");//
        }
    }
    class People{
        private String name;
        public People(){
            System.out.print("1");
        }
    
        public People(String name){
            System.out.print("2");
            this.name = name;
            System.out.println(name);
        }
    }
    class Child extends People{
        People father;
        
        //子类的构造器中,默认会调用父类的无参构造
        public Child(String name){
            System.out.print("3");
            father = new People(name +" F");
            
        }
        
        public Child(){
            System.out.print("4");
        }
    
    }
    
    打印:--->>
    132mike F
    View Code

    父类初始化< clinit >---->>>子类初始化< clinit >--->>  父类的实例初始化< init > --->>子类的实例初始化< init >;一开始都是静态的先初始化。

    (1.先父类初始化;2.子类初始化; 3.父类实例初始化; 4.子类实例初始化。)

    练习:

    * 1、实例初始化,为属性赋值的过程
     *     (1)父类的实例初始化方法<init>()
     *             x = 10;//父类中的x
     *             this.print();//要考虑方法的重写,因为在创建子类Son的对象,所以这个this是子类的对象
     *                     System.out.println("Son.x = " + x);// Son.x = 0  ,这个是子类中的x
     *             x = 20;//父类中的x
     *     (2)子类的实例初始化方法<init>()
     *             x = 30;//子类的x
     *             this.print();
     *                     System.out.println("Son.x = " + x);//子类中的x,就近原则    Son.x = 30
                x = 40;//子类的x
     *             
     * 2、属性没有多态
     *             System.out.println(f.x);//按照编译时类型,父类的x
     */
    public class Exam3 {
        public static void main(String[] args) {
            Father f = new Son();
            System.out.println(f.x);//先初始化最后执行这个,按照编译时类型,父类的x;属性没有多态。
        }
    }
    class Father{
        int x = 10;      //父类的实例初始化; 显式赋值;
        public Father(){ //构造器;
            this.print();//this指的是当前对象,即Son对象,它重写了print方法,所以调用的是子类的print方法
            x = 20;
        }
        public void print(){
            System.out.println("Father.x = " + x);
        }
    }
    class Son extends Father{
        int x = 30;
        public Son(){
            this.print();
            x = 40;
        }
        public void print(){                   //执行这个方法的时候x还没赋值,它的默认值为0
            System.out.println("Son.x = " + x);//子类中的x,就近原则  
        }
    }

    ----->>> 打印:

      Son.x = 0
      Son.x = 30
      20

    * 实例初始化的问题:
     * 1、创建父类对象时
     *     执行父类的实例初始化方法<init>
     *         System.out.println("father create");
     * 2、创建子类对象时
     *     执行父类的实例初始化方法<init>
     *         System.out.println("father create");
     *  执行子类的实例初始化方法<init>
     *      System.out.println("child create");
     */
    public class Child extends Father{
        public Child(){
            System.out.println("child create");
        }
        
        public static void main(String[] args) {
            Father f = new Father();   //先执行这一句,父类 f 的实例初始化 
            Child c = new Child();    //再执行子类的实例初始化 --> 它会导致父类的先初始化再初始化子类
        }
    }
    
    public class Father {
        public Father(){
            System.out.println("father create");
        }
    }
    
    打印:
    father create
    father create
    child create
     * 1、创建对象
     *     实例初始化
     * (1)父类的实例初始化<init>
     *         name = "father"; //这个name是父类的name
     * (2)子类的实例初始化<init>
     *         name = "test"; //这个name是子类的name
     * 
     * 2、对象调用方法
     *     调用的是子类从父类继承的getName()
     * 
     * 子类的对象有两个name,   this.name,如果在子类中,this.name就是子类的,如果在父类中,this.name就是父类的
     */
    public class Test extends Father{
        private String name = "test";
        
        public static void main(String[] args) {
            Test test = new Test();
            System.out.println(test.getName());
        }
    }
    
    public class Father {
        private String name = "father";
    
        public String getName() {
            return name;//这里就近原则,找的是父类的name
        }
    }
     * 1、创建对象
     *         Other o = new Other(); o.i = 0;
     * 2、调用了addOne(o)
     *         o.i++;        o.i = 1;
     * 3、final修饰的变量不可变问题
     *     是修饰什么变量,局部变量还是成员变量?
     */
    public class Something {
        public static void main(String[] args) {
            Other o = new Other();
            new Something().addOne(o); //对象.方法
            System.out.println(o.i);
        }    
        //final修饰的变量不可变;  这里是o变量不可变
        public void addOne(final Other o){
    //        o = new Other();//o的变量值不可变
            o.i++;
        }
    }
    class Other{
        public int i;
    }
     * 1、static
     *      本类中,静态的方法,代码块可以访问静态的属性
     * 2、类初始化
     *     main方法所在的类必须先初始化,然后才能执行main方法
     *    
     *    Myclass2类的初始化<clinit>()
     *        static{
                int x = 5;
                x--;//这里的x是局部变量的x,不是类变量的x  x = 4
            }
            static{
                x--;//这里的x是类变量的x  x = -1
            }
     * 在执行main
     *         System.out.println("x = " + x);//类变量的x   x=-1
            z--;//类变量的z   z= -1
            myMethod();
                y = z++ + ++z;//都是类变量
                (1)先把z=-1的值load到操作数栈(2)z++,z=0(3)++z,z=1(4)再把z=1的值load到操作数栈(5)计算-1+1(6)赋值给y=0
            System.out.println("result: " + (z + y + ++z));
                (1)先把z=1的值load到操作数栈(2)再把y=0的值load到操作数栈
                (3)++z,z=2(4)先把z=2的值load到操作数栈(5)执行1+0+2(6)打印3
     * 2、局部变量与成员变量(类变量、实例变量)
     * 3、自增问题
     * 
     */
    public class Myclass2 {
        static int x,y,z;
        static{
            int x = 5;  //局部变量
            x--;
        }
        static{
            x--;  //这里的x是类变量
        }
        public static void main(String[] args) {
            System.out.println("x = " + x);//类变量的x   x=-1
            z--;//类变量的z   z= -1
            myMethod();
            System.out.println("result: " + (z + y + ++z));
        }
        
        public static void myMethod(){
            y = z++ + ++z;
        }
    }
    
    x = -1
    result: 3
    ==和equals()的区别
    
    ==:如果是基本数据类型,比较的是数据值
    如果是引用数据类型,比较的是对象的地址值
    equals():必须是对象才能调用,它是Object类中声明的,如果子类没有重写,那么它的效果和==是一样的,也是比较对象的地址。如果子类重写了,
    那么就按照重写的规则进行比较,例如String类型重写了equals,比较的是字符串的字符内容。

    重写一个类的equals方法需要主意什么?

    (1)必须和hashCode()方法一起重写,凡是参与equals比较的属性,一定要参与hashCode值的计算。
    (2)equals的重写要求遵循几个原则:
    equals 方法在非空对象引用上实现相等关系: 
    •    自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。 
    •    对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。 
    •    传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。 
    •    一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。 
    •    对于任何非空引用值 x,x.equals(null) 都应返回 false
  • 相关阅读:
    2017ICPC南宁补题
    H. The Game of Life
    I
    Twice Equation
    (贪心+队列)String
    Marcin and Training Camp
    莫比乌斯函数模版
    HDU-1695 莫比乌斯反演
    Steps to One DP+莫比乌斯反演
    Educational Codeforces Round 62 (Rated for Div. 2)
  • 原文地址:https://www.cnblogs.com/shengyang17/p/10004038.html
Copyright © 2020-2023  润新知