zoukankan      html  css  js  c++  java
  • java面向对象基础(二)

    权限修饰符

    权限修饰符包括public、private、protected和不加任何修饰符的default,它们都可以修饰方法和变量。其中public和默认的default(不加任何修饰符)这两个还可以修饰class。private和protected修饰类的情况只能在使用内部类时修饰,正常情况下不能使用这两个修饰符修饰类。

    (1).public:用public修饰的变量及方法,包内及包外的任何类(包括子类和普通类)均可以访问;
    (2).protected:用protected修饰的变量及方法,包内的任何类及包外那些继承了该类的子类才能访问,protected重点突出继承;
    (3).default:没有用public、protected及private中任何一种修饰,其访问权限为default默认权限。默认访问权限的类、类属变量及方法,包内的任何类(包括继承了此类的子类)都可以访问它,而对于包外的任何类都不能访问它(包括包外继承了此类的子类)。default重点突出包;
    (4)private: 用private修饰的变量及方法,只有本类可以访问,而包内包外的任何类均不能访问它。

    就一句话:protected修饰符所修饰的变量和方法,只可以被子类访问,而不管子类是不是和父类位于同一个包中。default修饰符所修饰的变量和方法,只可被同一个包中的其他类访问,而不管其他类是不是该类的子类。protected属于包修饰符,还是子类修饰符,而default属于包修饰符。

    从权限严格角度来说,private < default < protected < public

    在考虑default修饰的权限时,它是包修饰符,其中没有加入到包中的"裸体类"属于同一个隐式的包中,因此可以互相访问。

    例如,前面的person和student的继承关系中,将父类成员变量name加上private修饰符,于是下面的代码将编译出错。因为new子类对象时,构造方法中赋值给this.name,而这个name是继承自父类的,它是private的。因此对于子类来说,这个成员变量属于能看到,不能引用、不能操作的摆设属性。

    class Person  {
        private String name;
        int age;
    
    }
    
    class Student extends Person {
        int studentID;
    
        Student(int id,String name,int age) {
            this.name = name;
            this.age = age;
            this.studentID = id;
        }
    
    }
    
    public class Inherit {
        public static void main(String[] args) {
            Student s1 = new Student(1,"Malongshuai",23);
        }
    }
    

    方法的重写(overwrite/override)

    父类定义的成员相对来说都比较粗糙,当子类继承时,难免无法适当地描述子类。因此当子类对从父类继承的方法不满意时,可以重写方法。

    例如Person类能eat(),但girl类吃饭是淑女的吃,boy类吃饭是粗鲁的吃。girl类很不满意,因为父类的eat()只能描述吃,不能描述怎么吃。于是girl类就重写eat()方法,让吃这个方法符合自身的淑女形象。

    重写方法必须和被重写的方法具有相同的方法名称、参数列表和返回类型。重写的方法不能比被重写的方法权限更严格。从方法访问的角度来说,父类的方法都能被访问,子类重写后的方法却不能被访问,这显然是不合理的,且即使这是能访问父类方法,但重写的意义就丢失了。

    重写方法时,最佳实践方式是copy整个被重写的方法的定义语句。因为即使重写方法的名称改变了,编译也不会出错。例如重写eat()结果写成了Eat(),编译是不会有任何错误出现的,此时它没有重写,而是新定义了一个Eat()方法。

    class Person  {
        String name;
        int age;
    
        void eat() { System.out.println("eating...");}
    }
    
    class Student extends Person {
        int studentID;
    
        Student(int id,String name,int age) {
            this.name = name;
            this.age = age;
            this.studentID = id;
        }
    
        void eat() { System.out.println("graceful eating");}  //重写
        void study() {System.out.println("studing...");}
    }
    
    public class Inherit {
        public static void main(String[] args) {
            Student s1 = new Student(1,"Malongshuai",23);
            System.out.println(s1.studentID+","+s1.name+","+s1.age);
            s1.eat();  //调用重写后的eat方法
        }
    }
    

    super关键字

    this关键字指向对象自身,而super关键字则指向对象中的父对象。如下图:

    super既可以用来引用父对象的成员变量,也可以用来调用父对象的方法。例如下面的代码:

    class FatherClass {
        public int value;
        public void f(){
            value = 100;
            System.out.println("FatherClass.value="+value);
        }
    }
    
    class ChildClass extends FatherClass {
        public int value;
        public void f() {
            super.f();       //虽然f()要重写,但父对象的f()函数还有一些用武之地来发挥余热
            value = 200;
            System.out.println("ChildClass.value="+value);
            System.out.println(value);
            System.out.println(super.value);
        }
    }
    
    public class TestInherit {
        public static void main(String[] args) {
            ChildClass cc = new ChildClass();
            cc.f();
        }
    }
    

    new出子对象时,父类和子类中都有value属性,它们都采用的初始化值0。当执行cc.f()时,调用子类的f()方法,该方法首先调用父对象的f()方法,父f()方法先将value赋值为100,这个value是父对象的属性,然后回到子f()中赋值value为200,这个value是子对象自身的value,随后输出的两个value都是子对象中的value属性,最后的super.value是父对象中的value属性。

    虽然在图中看上去super和this的地位是相同的,但实际上它们之间很不公平,不公平之处在于有引用变量(上图中的cc)指向子对象,所以能够使用"return this"代码来返回一个子对象,但却不能使用"return super"来返回子对象中的父对象,因为没有引用变量指向父对象。

    关于super调用的成员变量,需要区分清楚是子对象中的属性还是父对象中的属性。如果子对象和父对象中有同名属性var,在没有指定"this.var"和"super.var"时,仅模糊地指定var时将优先取子对象的属性,如果子对象中没有某属性,则var表示的是父对象中的属性。例如:

    class Student extends Person {
        int studentID;
        int age = 33;
    
        Student(int id) {
            this.name = super.name + "x";
            this.age = age + 2;   //右边的age是子对象的属性,但如果将"int age = 33;"注释,则age是父对象的属性
            this.studentID = id;
        }
    }
    

    继承时构造方法的重写super()

    子对象中总是包含父对象,这个父对象是怎么来的?对象都是通过构造方法构造出来的,因此在new子类对象的时候,会调用对应的子类构造方法构造子对象,正是这个时候使用super()方法表示调用父类构造方法将父对象构造出来的。

    在写构造父对象的代码时有以下几个规则:

    1. 使用子类构造方法构造子对象时,必须要构造父对象。
    2. 子类可以在自己的构造方法中使用super(args)来调用父类的构造方法。同理,可以使用this(args)来调用本类其他的构造方法。
    3. super(args)必须写在子类构造方法中的第一行,因为要先构造出父对象,再慢慢填补子对象自身。如果没有显式书写super(args),则默认在第一行处调用父类无参数的构造方法,等价于super()。
    4. 如果子类构造方法中调用的super(args)在父类中不存在对应参数列表的构造方法,则编译错处。这包括没有显式指定super()时,且父类又重载了构造方法使得父类中没有了无参数的构造方法时。
    class Person {
        String name;
        int age;
    
        Person() {
            System.out.println("Person()");
        }
    
        Person(String name,int age) {
            this.name = name;
            this.age = age;
            System.out.println("Person(arg1,arg2)");
        }
    
        Person(String name) {
            this.name = name;
            age = 20;
            System.out.println("Person(arg1)");
        } 
    }
    
    class Student extends Person {
        int studentID;
    
        Student(int id) {
            super("Malongshuai",23);   //第一行调用父类构造方法构造父对象,且是含有两个参数的Person(arg1,arg2)
            this.name = super.name + "X"; //调用父对象中的name属性
            this.age = age + 2;           //也是调用父对象中的属性age
            this.studentID = id;
        }
    }
    
    public class TestSuper {
        public static void main(String[] args) {
            Student s1 = new Student(1);
            System.out.println(s1.studentID+", "+s1.name+", "+s1.age);
        }
    }
    

    如果将"super("Malongshuai",23);"修改为super("Malongshuai"),则表示调用父类的Person(arg1)构造方法。如果改为super(),则表示调用父类的Person()构造方法。如果省略不写super,则等价于super()。

    Object类

    除了明确定义了从某个父类继承的子类,java中的所有类都是从java.lang包中的Object类继承来的。也就是说,Object类是所有类继承的根,也就是它们的祖宗。一级继承一级,最终的根总是Object类。

    这个类里提供了几个方法,但基本上所有方法都建议重写,因为它的级别太高,抽象化的太严重,它的提供的那些方法也就太大众化。

    toString()

    在和对象做数据连接时,将自动调用该类的toString()方法。例如System.out.println("Hello" + Person)时,等价于System.out.println("Hello" + Person.toString())

    例如:

    public class TTString {
        public static void main(String [] args) {
            Person p = new Person();
            System.out.println(p);
            System.out.println(p.toString());
        }
    }
    
    class Person {}
    

    编译并运行,查看toString()的运行结果。

    D:myjava
    λ javac TTString.java
    
    D:myjava
    λ java TTString
    Person@15db9742
    Person@15db9742
    

    toString()的结果是"类名@hex(hashcode)"。官方建议,任何子类都应该重写该方法。例如:

    public class TTString {
        public static void main(String [] args) {
            Person p = new Person();
            System.out.println(p);
        }
    }
    
    class Person {
        public String toString() {  //重写toString()
            return "Hello World";
        }
    }
    

    对象的比较"=="和equals()

    对象与对象之间是否有相等关系?一般可以认为,如果两个对象的对象内容完全相同,将认为是相等的对象。

    在进行对象比较时,"=="比较的是两个对象的引用地址,因此两个对象使用"=="比较时是绝对不会相等的。Object类中的equals(),基本等价于"==",因此也无法正确比较对象是否相等。所以官方手册建议重写equals()方法。在String类中已经重写好了。

    例如,使用String类中的equals()。

    public class TTequals {
        public static void main(String [] args) {
            String s1 = new String("hello");
            String s2 = new String("hello");
            System.out.println(s1 == s2);
            System.out.println(s1.equals(s2));
        }
    }
    

    final关键字

    final表示最终的意思,它可以修饰变量、方法和类。

    1. final变量的值不能被改变。
      • (1).final的成员变量不可改变。
      • (2).final的局部变量和形参不可改变。
    2. final的方法不能被重写。
    3. final的类不能被继承。

    也就是说,要想让变量只读、方法不可改变或类的继承到此结束,就用final进行修饰。

    注:若您觉得这篇文章还不错请点击右下角推荐,您的支持能激发作者更大的写作热情,非常感谢!

  • 相关阅读:
    Djano restframework
    python测试一
    SQL分类,DDL,DML,DCL
    sql查询时,根据特定的条件给表的某一个字段赋值
    数据类型之Nullable
    web.config节点
    拼凑的宿主-host
    css的优先级
    jquery——write less,do more
    double类型计算
  • 原文地址:https://www.cnblogs.com/f-ck-need-u/p/8127546.html
Copyright © 2011-2022 走看看