继承、抽象类、Object类、枚举、反射

目录

一 继承:

1.1 extends关键字

1.2 super关键字

1.3 protected关键字:

1.4 方法重写

1.5 多态

1.6 fine关键字

二 抽象类

三 Object类

3.1 equals方法:

3.2 hashCode方法:

3.3 toString方法:

四 枚举类

4.1 构造器:

4.2 常用方法:

4.3 单例模式:

五 包装器类

5.2 自动装箱

5.3 自动拆箱

六 参数可变方法

七 反射

7.1 反射相关的类

7.2 Class类

7.3 Field类

7.4 Method类

7.5 Constructor类

7.6 反射示例


一 继承:

基本语法 :
class 子类 extends 父类 { 
 
}
  • 使用 extends 指定父类.
  • Java 中一个子类只能继承一个父类 (C++/Python等语言支持多继承).
  • 子类会继承父类的所有 public 的字段和方法.
  • 对于父类的 private 的字段和方法, 子类中是无法访问的.
  • 子类的实例中, 也包含着父类的实例. 可以使用 super 关键字得到父类实例的引用

1.1 extends关键字

        关键字extends用于表示类与类的关系,表明构造的新类派生于一个已经存在的类,其中新类称为: 子类/派生类/孩子类,已存在的类称为: 超类/基类/父类

1.2 super关键字

super构造器:

class Animal { 
 public String name; 
 public Animal(String name) { 
     this.name = name; 
 } 

}
class Cat extends Animal { 
 public Cat(String name) { 
     // 使用 super 调用父类的构造方法. 
     super(name); 
 } 
}

        子类继承父类时,必须先实现父类的构造方法。super()表示父类的构造方法,必须放在子类构造函数的第一行。

super引用:

class Employee {
    public double getSalary() {
        return 300d;
    }
}

class Manager extends Employee {
    public double getbonus() {
        return super.getSalary();  //调用父类的getSalary()
        //return getSalary();      //等价于super.getSalary()
    }
}

super与this的关系:

1.3 protected关键字:

  •  对于包外的其他类来说, protected 修饰的字段和方法是不能访问的
  • 对于类的 子类 同一个包的其他类 来说, protected 修饰的字段和方法是可以访问的

与其他访问修饰符的区别:

1.4 方法重写

        当父类方法不适用与子类方法时,可以重写父类的方法

示例:

class Employee {
    public double getSalary() {
        return 300d;
    }
}

class Manager extends Employee {
    public double getSalary() {
        //此处不能写成getSalary()+1000;不然会造成死循环
        return super.getSalary()+1000;   
    }
}
        子类实现父类的同名方法, 并且参数的类型和个数完全相同 , 这种情况称为 覆写 / 重写 / 覆盖 (Override)
重写注意事项:
  • 普通方法可以重写, static 修饰的静态方法不能重写.
  • 重写中子类的方法的访问权限不能低于父类的方法访问权限.
  • 重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同, 特殊情况除外)

重载与重写区别:

在构造方法中调用重写的方法 ( 一个坑 ):
class B { 
 public B() { 
     func(); 
 } 
 public void func() { 
     System.out.println("我是B"); 
 } 
} 
class D extends B { 
 private int num = 1; 
 @Override 
 public void func() { 
     System.out.println("我是D:" + num); 
 } 
} 
public class Test { 
 public static void main(String[] args) { 
     D d = new D();    //执行结果: 我是D:0
 } 
}
  • 构造 D 对象的同时, 会调用 B 的构造方法.
  • B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
  • 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为0

可协变返回类型示例:

class Employee {
    public Employee getBuddy() {
        return new Employee();
    }
}

class Manager extends Employee {
    public Manager getBuddy() {
        return new Manager();
    }
}

        上述例子中, 同名方法getBuddy()构造重写,子类的方法返回类型是父类方法返回类型的子类

1.5 多态

向上转型示例:
class Employee {
}

class Manager extends Employee {
    public static void main(String[] args) {
        Employee e = new Manager();    //多态,父类可以引用子类对象
    }
}

        此时的实例对象是Manager类型,但e是Employee类型的引用,因此只能使用Employee类所拥有的方法。

动态绑定示例:

class Employee {
    public double getSalary() {
        return 300d;
    }
}

class Manager extends Employee {
    public double getSalary() {
        //此处不能写成getSalary()+1000;不然会造成死循环
        return super.getSalary() + 1000;
    }

    public static void main(String[] args) {
        Employee e = new Manager();
        System.out.println(e.getSalary());  //输出: 1300.0
    }
}
  •  一个对象变量(如: 变量e) 可以指示多种实际类型的现象称为多态(polymorphism)。
  • 运行时能够自动选择适当的方法,称为动态绑定

静态绑定:

        如果是private方法、static方法、final方法、构造器,那么编译器可以准确的知道调用那个方法,称为静态绑定。

向下转型示例:

class Employee {
    public double getSalary() {
        return 300d;
    }
}
class Manager extends Employee {
    public double getSalary() {
        return super.getSalary() + 1000;
    }

    public double getbond() {
        return 1000;
    }

    public static void main(String[] args) {
        Employee e = new Manager();
        //e.getbond();       //不能调用getbond()方法
        Manager m = (Manager) e; //向下转型
        m.getbond();
    }
}
注意事项:
  • 编译器检查有哪些方法存在, 看的是 Employee 这个类型
  • 执行时究竟执行父类的方法还是子类的方法, 看的是 Manager 这个类型(动态绑定)

强制转化:

        向下转化时,可以先判断是否能够成功转化, 可以使用 instanceof 操作符。

示例:

class Employee {
    
}

class Manager extends Employee {
    public static void main(String[] args) {
        Employee e = new Manager();
        Manager m;
        if (e instanceof Manager)
            m = (Manager) e;
    }
}

1.6 fine关键字

  • 修饰基本数据类型的变量: 表示常量 (不能修改)。
  • 修饰引用变量: 表示引用的对象不能改变
  • 修饰类: 表示该类不能被继承。
  • 修饰方法: 表示方法不能被重写

二 抽象类

语法规则 :

abstract class Shape { 
 abstract public void draw(); 
}
  • abstract 关键字修饰的方法是一个抽象方法. 抽象方法没有方法体, 不能执行具体代码。
  • 对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类。
  • 抽象类不能直接实例化。
  • 抽象方法不能是 private
  • 抽象类中可以包含其他的非抽象方法和字段

三 Object类

        Object类是Java中所有类的始祖,每个类都默认继承了Object类

示例:

class Manager {
    public static void main(String[] args) {
        Object o = new Manager();
    }
}

3.1 equals方法:

Object原码:

 public boolean equals(Object obj) {
        return (this == obj);
    }

        自定义类想要比较内容相等,需要重写equals方法。

示例:

class Employee {
    double salary;
    String name;
}
class Manager extends Employee {
    @Override
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (this == obj)
            return true;
        if (this.getClass() != obj.getClass()) //子类的语义(需要比较的关键字段)不相同时
            return false;
//        if(!(obj instanceof Employee))       //子类语义相同时,根据实际情况,与上一条if二选一
//            return false;
        return this.salary == ((Manager) obj).salary         //基本数据类型直接用==判断
                && this.name.equals(((Employee) obj).name);  //引用类型用equals()判断
    }
}

Objects.equals静态方法:

    public static boolean equals(Object a, Object b) {  //Objects.equals方法原码
        return (a == b) || (a != null && a.equals(b));
    }

        如果两个对象a,b都可能为空,则使用Objects.equals静态方法。

3.2 hashCode方法:

        自定义类若没有重写hashCode方法,则默认使用Object类的hashCode方法,从对象的存储地址得散列码。

String类的hashCode方法:

    public int hashCode() {
        int h = hash;  //hash Default to 0
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

3.3 toString方法:

Object原码:

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

示例:

class Employee {
    public static void main(String[] args) {
        Employee e = new Employee();
        String str = e+"";      //默认e.toString();
        System.out.println(e);  //默认e.toString();
    }
}

数组类的toString()方法:

        一维数组: Arrays.toString方法

        多维数组: Arrays.deepToString方法

编辑器自动生成toString方法:

1) 鼠标右键

 2) 点击Generate

 3) 选择toString()

 4) 选择ok

四 枚举类

示例:

public enum TestEnum {
     RED,BLACK,GREEN; 
}

备注: 默认继承Enum类。

4.1 构造器:

public enum TestEnum {
    RED("red", 1), BLACK("black", 2), WHITE("white", 3), GREEN("green", 4);
    private String name;
    private int key;

    private TestEnum(String name, int key) {
        this.name = name;
        this.key = key;
    }

    public static TestEnum getEnumKey(int key) {
        for (TestEnum t : TestEnum.values()) {
            if (t.key == key) {
                return t;
            }
        }
        return null;
    }

    public static void main(String[] args) {
        System.out.println(getEnumKey(2));
    }
}

 备注: 枚举的构造方法默认是私有的

在switch中使用:

public enum TestEnum {
            RED, BLACK, GREEN, WHITE;

            public static void main(String[] args) {
                TestEnum testEnum2 = TestEnum.BLACK;
                switch (testEnum2) {
                    case RED:
                        System.out.println("red");
                        break;
                    case BLACK:
                        System.out.println("black");
                        break;
                    case WHITE:
                        System.out.println("WHITE");
                        break;
                    case GREEN:
                        System.out.println("black");
                        break;
                    default:
                        break;
                }
            }
        }

4.2 常用方法:

方法名称
描述
values()
以数组形式返回枚举类型的所有成员
compareTo()
比较两个枚举成员在定义时的顺序
ordinal()
获取枚举成员的索引位置
valueOf()
将普通字符串转换为枚举实例
toString()返回枚举常量名

4.3 单例模式:

1)普通单例模式:

public class Singleton {
    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {  //进入区域后,再检查一次,如果仍是null,才创建实例 
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

 静态内部类实现单例模式:

class Singleton {
    //私有化构造器
    private Singleton() {
    }

    //对外提供公共的访问方法
    public static Singleton getInstance() {
        return UserSingletonHolder.INSTANCE;
    }

    //写一个静态内部类,里面实例化外部类
    private static class UserSingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

public class Main {
    public static void main(String[] args) {
        Singleton u1 = Singleton.getInstance();
        Singleton u2 = Singleton.getInstance();
        System.out.println("两个实例是否相同:" + (u1 == u2));
    }
}

枚举实现单利模式:

public enum TestEnum {
    INSTANCE;

    public TestEnum getInstance() {
        return INSTANCE;
    }

    public static void main(String[] args) {
        TestEnum singleton1 = TestEnum.INSTANCE;
        TestEnum singleton2 = TestEnum.INSTANCE;
        System.out.println("两个实例是否相同:" + (singleton1 == singleton2));
    }
}

枚举实现单例模式是安全的:

  • 枚举可以避免反射和序列化问题

五 包装器类

5.1 基本类型的包装器类:

包装器基本类型
Integerint
Longlong
Floatfloat
Doubledouble
Shortshort
Bytebyte
Characterchar
Booleanboolean

注意事项:

  • 前6个类派生于公共的超类Number
  • 包装器类是不可变的,一旦构造了包装器,就不允许更改包装在其中的值
  • 包装器类被final修饰, 不能派生他们的子类

5.2 自动装箱

示例:

    public static void main(String[] args) {
        Integer a = 3; //等价于a=Integer.valueOf(3)
    }

        int类型的3赋值给Integer对象a,自动调用Integer.valueOf方法,这个过程叫自动装箱

5.3 自动拆箱

示例:

    public static void main(String[] args) {
        Integer a = new Integer(3);
        int b = a;     //等价于b=a.intValue();
    }

        Integer对象变量的值赋值给int类型的b变量,自动调用intValue()方法,这个过程叫自动拆箱

六 参数可变方法

System.out.printf方法原码:

public PrintStream printf(String format, Object ... args) {
        return format(format, args);
    }

        上述方法的参数列表中, ...是代码一部分,表明这个方法的args参数可以接收任意数量的对象。

自定义参数可变方法:

class Employee {
    public static void main(String[] args) {
        System.out.println(add());       //输出: 0
        System.out.println(add(1));      //输出: 1
        System.out.println(add(1, 2));   //输出: 3
        System.out.println(add(1, 2, 3));//输出: 6
        System.out.println(add(new int[]{1, 2, 3, 4})); //输出: 10
    }

    public static int add(int... a) {
        int sum = 0;
        for (int x : a)
            sum += x;
        return sum;
    }
}

七 反射

定义:

        Java的反射(reflflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法

7.1 反射相关的类

类名
用途
Class
代表类的实体,在运行的 Java 应用程序中表示类和接口
Field
代表类的成员变量 / 类的属性
Method
代表类的方法
Constructor
代表类的构造方法

 7.2 Class类:

        Java文件被编译后,生成 .class 文件,由 JVM 解读 .class 文件,解析为一个对象, 这个对象的类型是java.lang.Class . 程序在运行时,每个 java 文件最终变成 Class 类对象的一个 实例。通过Java 的反射机制拿到到这个实例,就可以去 获得甚至去添加改变这个类的属性和动作 ,使得这个类成为一个动态的类。

相关方法:

 获得Class对象的三种方式:

1) Class.forName("类的全路径名"):
Class c3 = null; 
try {
 c3 = Class.forName("Student"); //注意这里是类的全路径,如果有包需要加包的路径
} catch (ClassNotFoundException e) {
 e.printStackTrace(); 
}
2) 类名.class ():
Class c2 = Student.class;//最为安全可靠,任何一个类都有一个隐含的静态成员变量 class
3) 使用类对象的 getClass() 方法:
Student s1 = new Student(); 
Class c1 = s1.getClass();
备注: 一个类在 JVM 中只会有一个 Class 实例

7.3 Field

相关方法:

7.4 Method

相关方法:

7.5 Constructor

 相关方法:

7.6 反射示例:

class Student {
    //私有属性name
    private String name = "毛毛";
    //公有属性age
    public int age = 18;

    //不带参数的构造方法
    public Student() {
        System.out.println("Student()");
    }
    private Student(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }
    private void function(String str) {
        System.out.println(str);
    }

    @Override
    public String toString() {
        return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

class lan {
    public static void main(String[] args) {
        reflectPrivateConstructor();
         reflectPrivateField();
         reflectPrivateMethod();
    }

    // 反射私有的构造方法
    public static void reflectPrivateConstructor() {
        try {
            //获得Class对象
            Class<?> classStudent = Class.forName("Cctalk_Demo.Student");
            //通过Class对象.获得构造对象
            Constructor<?> constructor = classStudent.getDeclaredConstructor(String.class, int.class);
            //设置为true后可修改访问权限
            constructor.setAccessible(true);
            //通过构造器对象的方法创建实例
            Object object = constructor.newInstance("毛哥", 16);
            Student student = (Student) object;
            System.out.println(student);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 反射私有属性
    public static void reflectPrivateField() {
        try {
            Class<?> classStudent = Class.forName("Cctalk_Demo.Student");
            Field field = classStudent.getDeclaredField("name");
            field.setAccessible(true);
            //可以修改该属性的值
            Student student = (Student) classStudent.newInstance();
            field.set(student, "叫我毛毛!");
            System.out.println((String) field.get(student));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 反射私有方法
    public static void reflectPrivateMethod() {
        try {
            Class<?> classStudent = Class.forName("Cctalk_Demo.Student");
            Method method=classStudent.getDeclaredMethod("function",String.class);
            method.setAccessible(true);
            Student student=(Student) classStudent.newInstance();
            method.invoke(student,"我叫吉吉!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

备注:

  • 使用反射会有效率问题。会导致程序效率降低。
  • 反射技术绕过了源代码的技术,带来维护问题。
  • 反射代码比相应的直接代码更复杂
  • 枚举类不可反射

在JDK1.5 之前,我们定义常量都是: publicstaticfianl.... 。现在好了,有了枚举,可以把相关的常量分组到一个枚举型里,而且枚举提供了比常量更多的方法。 public enum Color { RED, GREEN, BLANK, YELLOW } 用法二:switch JDK1.6之前的switch语句只支持int,char,enum型,使用枚举,能让我们的代码可读性更强。 enum Signal { GREEN, YELLOW, RED } public class TrafficLight { Signal color = Signal.RED; public void change() { switch (color) { case RED: color = Signal.GREEN; break ; case YELLOW: color = Signal.RED; break ; case GREEN: color = Signal.YELLOW; break ; } } } 用法三:向枚举中添加新方法 如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum实例。 public enum Color { RED("红色" , 1 ), GREEN( "绿色" , 2 ), BLANK( "白色" , 3 ), YELLO( "黄色" , 4 ); // 成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this .name = name; this .index = index; } // 普通方法 public static String getName( int index) { for (Color c : Color.values()) { if (c.getIndex() == index) { return c.name; } } return null ; } // get set 方法 public String getName() { return name; } public void setName(String name) { this .name = name; } public int getIndex() { return index; } public void setIndex( int index) { this .index = index; } } 用法四:覆盖枚举的方法 下面给出一个toString()方法覆盖的例子。 public enum Color { RED("红色" , 1 ), GREEN( "绿色" , 2 ), BLANK( "白色" , 3 ), YELLO( "黄色" , 4 ); // 成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this .name = name; this .index = index; } //覆盖方法 @Override public String toString() { return this .index+ "_" + this .name; } } 用法五:实现接口 所有的枚举继承java.lang.Enum。由于Java 不支持多继承,所以枚举对象不能再继承其他。 public interface Behaviour { void print(); String getInfo(); } public enum Color implements Behaviour{ RED("红色" , 1 ), GREEN( "绿色" , 2 ), BLANK( "白色" , 3 ), YELLO( "黄色" , 4 ); // 成员变量 private String name; private int index; // 构造方法 private Color(String name, int index) { this .name = name; this .index = index; } //接口方法 @Override public String getInfo() { return this .name; } //接口方法 @Override public void print() { System.out.println(this .index+ ":" + this .name); } } 用法六:使用接口组织枚举 public interface Food { enum Coffee implements Food{ BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO } enum Dessert implements Food{ FRUIT, CAKE, GELATO } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值