Fork me on GitHub

JavaSE| 面向对象的三大特征

 

面向对象的基本特征

1、封装Encapsulation

目的:隐藏实现细节,让使用者方便,让代码更安全

 将对象的属性和行为封装起来,其载体就是类。类通常对客户隐藏其实现细节,这就是封装的思想。

封装(Encapsulation):把该隐藏的隐藏起来,该暴露的暴露出来。

封装需要通过访问控制修饰符(权限修饰符)来实现。

/*
权限修饰符:
                本类    其他类(本包的其他类、其他包的子类、其他包的非子类)
private            √                   ×         ×            ×   (本类)
缺省(省略)       √                   √         ×            ×  (同包同类)
protected(受保护的)√                   √         √            ×   (同类同包子类,本类 + 本包其他类 + 其他包的子类 )
public             √                   √         √            √

权限修饰符的作用:限定某个类型、成员的可访问的范围、可见性的范围

权限修饰符可以修饰什么?
private:成员(属性、方法、构造器、内部类)
缺省:外部类等类型、成员(属性、方法、构造器、内部类)
protected:成员(属性、方法、构造器、内部类)
public:外部类等类型、成员(属性、方法、构造器、内部类)
    当public修饰外部类时,要注意类名必须与源文件名一致,即一个源文件只能有一个外部的public类

修饰符的学习:(1)可以修饰什么(2)修饰后有什么影响
*/
//外部类
public class TestModifier{
    //内部类
    public class Inner{
        
    }
}

所有的类默认继承Object

 访问权限:权利和限制;   方法的提供者和方法的调用者

Java中所谓的权限问题,其实就是对象属性或方法的提供者调用者之间的关系(同类,同包,子类)问题

对象当中的是指从属关系,不是调用的意思;

// clone方法的提供者:com.atguigu.testjava.User
// 方法的调用者:com.atguigu.testjava.TestJava
这两个类都继承了1个父类Object,但它们不是子类的关系(它们两个的父类不是一个爸爸),所以虽是protected修饰的clone,若User不重写clone方法,user对象是不能调用的
package com.atguigu.testjava;
public class TestJava {
    public static void main(String[] args) throws Exception {
        User user = new User();
        // clone方法的提供者:com.atguigu.testjava.User
        // 方法的调用者:com.atguigu.testjava.TestJava
        //这两个类都继承了1个父类Object,但它们不是子类的关系(它们两个的父类不是一个爸爸),所以虽是protected修饰的clone,若User不重写clone方法,user对象是不能调用的
        user.clone(); //
    }
}
/**User类继承Object
 * protected native Object clone()
 */
class User{
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
View Code
方法重写:JVM的动态绑定技术(https://www.cnblogs.com/shengyang17/p/10010418.html),所以对象user会去找子类重写的方法; 子类中没有就去父类中;

2、继承(inheritance)

  继承:延续,保留,并且扩展 extends
  目的:代码的复用和功能扩展; 继承还可以表示is-a的逻辑关系;Student is a Person.   Apple is a Fruit.

 2、如何继承?
  【修饰符】 class 子类名 extends 父类名{
   }
  
  子类:SubClass,也称为派生类;     父类:SuperClass,也称为超类、基类

 3、继承的特点
* (1)子类继承父类时,会继承父类的所有的属性、方法。* 但是因为修饰符的原因,某些属性和方法在子类中不可见。
* (2)子类“不会”继承父类的构造器;
* (3)在子类的构造器中一定会“调用”父类的构造器,并且默认调用的是父类的“无参”构造器

    如果父类没有“无参”构造,那么子类必须手动调用父类的“有参”构造。(子类在创建对象时候默认调用父类的无参构造器)
* (4)如果一个类没有显式的声明它的父类,那么它有一个默认的父类:java.lang.Object
* (5)在Java中,类的继承有“单继承限制”;意思:每一个子类只能有一个直接的父类,类似于每个人只有一个亲生父亲;makes code more reliable. 
* (6)Java中支持多层继承,即父类仍然可以有父类,子类会继承所有父类的属性和方法。 意思:代代相传
* (7)一个父类却同时可以有很多的子类,而且子类还可以有很多子类;* 意思:子孙满堂
* (8)子类可以扩展父类没有的属性和方法

方法的重写:Override 
* 方法的重写:当子类继承了父类的方法,但是父类的某个方法的方法体实现不适合于子类,那么我们子类可以选择进行重写。
 要求:
  (1)子类重写父类的方法,方法名必须一样;2)子类重写父类的方法,形参列表必须一样
  (3)子类重写父类的方法,返回值类型有要求:
      如果返回值类型是基本数据类型和void,那么要求必须完全一致;
      如果返回值类型是引用数据类型,那么要求<=的关系
  (4)子类重写父类的方法,权限修饰符的范围要求>=的关系;    
  (5)抛出的异常列表的类型:<= 子类重写方法抛出的异常类型 <= 父类被重写方法抛出的异常类型
在子类中,如果要调用父类被重写的方法,可以使用“super. "被重写方法”

 

...略
   public String getInfo(){
        return "姓名:" + name + "\t薪资:" + salary; 
    }
    public int test(){
        return 0;
    }
    public Object method(){
        return null;
    }
    protected void function(){
        
    }
    
}

class Manager extends Emplyee{
    private double bonus;
    
    /*public String getInfo(){
        return "姓名:"getName() + "\t薪资:" + getSalary() + "\t奖金:" +bonus;
    }*/
    public String getInfo(){
        return super.getInfo() + "\t奖金:" +bonus;
    }
    
    public String method(){  //String < Object 类型
        return null;
    }
    public void function(){
        
    }
    
}

Java中方法重写是基于JVM的动态绑定技术:调用对象的成员方法(直接or间接都可以)时,JVM会将对象的实际内存和当前的方法进行绑定。

成员变量没有动态绑定操作,成员变量的调用是在哪里声明在哪里使用

//40 调用a对象的成员方法,JVM会将对象的实际内存(实际内存是new B())和当前的方法-实际内存中的方法getResult()进行绑定。这是是有两个一模一样的变量 i的,那么它-对象a为什么调用自己的i了呢,因为省略了this.i,如果要调用父类的i就要super.i 
public class TestOverride {
    public static void main(String[] args) {
        A a = new B(); //多态
        System.out.println(a.getResult()); //40 调用a对象的成员方法,JVM会将对象的实际内存(实际内存是new B())和当前的方法getResult()进行绑定。
    }
}
class A {
    public int i = 10;
    public int getResult() {
        return i + 10;
    }
}
class B extends A {
    public int i = 20;
    public int getResult() {
        return i + 20;
    }  //
}
View Code

 //30 如果把当前对象实际内存中的方法注释掉,则它就会去找父类中的 getResult( )方法---是成员变量,没有动态绑定操作;这时是只有一个变量 i;

public class TestOverride {
    public static void main(String[] args) {
        A a = new B(); //多态
        System.out.println(a.getResult());
    }
}
class A {
    public int i = 10;
    public int getResult() {
        return i + 20;
    }
}
class B extends A {
    public int i = 20;
/*    public int getResult() {
        return this.i + 20;
    }*/  //
}
View Code

 //40 调用对象的成员方法,对象new B()中实际内存和当前方法即B类中方法getResult(),B类中没有重写它是继承父类中getResult,父类中的getInt()也是属于调用对象的成员方法,它会在B类中找这个方法(动态绑定)

public class TestOverride {
    public static void main(String[] args) {
        A a = new B(); //多态
        System.out.println(a.getResult());
    }
}
class A {
    public int i = 10;
    public int getResult() {
        return getInt() + 20;
    }
    public int getInt(){
        return i;
    }
}
class B extends A {
    public int i = 20;
/*    public int getResult() {
        return this.i + 20;
    }*/  //
    public int getInt(){
        return i;
    }
}
View Code

基于动态绑定---->模板方法设计模式--即上边写的代码; HttpServlet也是这种模式;父类把方法业务的骨架搭建好,提供模板,把结果返回,子类重写模板的细节;

 

* this关键字:当前对象
 * (1)this.属性
 * 当成员变量与局部变量重名时,在成员变量前面加"this."
 * 
 * (2)this.方法
 * 表示调用当前对象的成员方法,可以省略this.
 * 
 * (3)this()或this(实参列表) 表示访问本类的其他构造器;
 * this()或this(实参列表);   this()调用本类的无参构造。
 * 
 * super关键字:父类的  
 * 前提:要通过super调用父类的属性、方法、构造器,必须要求他们的可见性是在子类中可见的。
 * 
 * (1)super.属性
 *子类的属性与父类的属性重名时,可以使用super.属性 表示父类的属性。
 * 
 * (2)super.方法 
 * 当子类重写了父类的方法,又想调用父类被重写的方法,那么可以使用“super.父类的方法名 被重写方法”
 * 
 * (3)super()或super(实参列表)
 * super()表示调用父类的无参构造器,可以省略。   子类继承父类(子类、父类两者都有无参构造和有参构造,子类在进行创建对象时,不管有没有传参,
都是默认调用父类的无参构造;
如果是传参数了,它只是传给了它自己的构造器。
super(实参列表)表示调用父类的有参构造,不可以省略,特别是父类没有无参构造时
* 要求:和this()、this(实参列表)一样,必须在构造器的首行。
    public Manager() {
        super();
    }

    public Manager(String name, double salary, double bonus) {
        super(name, salary);   //调用父类的有参构造   加上   子类自己扩展的属性。
        this.bonus = bonus;
    }

    if(amount < balance){
            //正常取
        super.withdraw(amount); //调用父类的withdraw方法;方法的重写。
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
View Code

 

this的追溯不仅限于当前类,也可以是从父类继承的属性、方法,只要可见。
 * 如果子类中有和父类一样的属性,那么this.就代表子类的,
 * 如果子类重写的父类的方法,那么this.就代表子类的。

* this表示当前对象       “运行时的类型”
 * (1)在构造器中,this代表的是正在new的那个对象
 * (2)在成员方法中,this代表的是调用该方法的那个对象

* this,先从当前类中开始找
 * (1)一种如果this代表子类的对象,那么还要检查子类是否重写
 * (2)另一种如果this代表的是本类的对象,那么直接从本类中查找
 * super,先从直接父类开始找,如果直接父类没有,一直往上
public class TestThis2 {
    public static void main(String[] args) {
        //Father f = new Son();//这句代码在new子类Son的对象,所以这个this代表子类的对象
        //子类的对象执行test()一定是子类重写的代码
        
        //Father f2 = new Father();//这句代码在new父类的对象,所以这个this代表父类的对象
    
        Father f3 = new Son();
        f3.method();//f3运行时,代表的是子类的对象
    }
}
class Father{
    //(1)父类的无参构造会不会执行?会,因为子类的默认无参构造中默认调用父类的无参构造super()
    public Father(){
        //(2)这个test()执行的是哪个?
        test();//等价于this.test()
    }
    public void test(){
        System.out.println("父类的test()方法");
    }
    
    public void method(){
        System.out.println("父类的method()方法");
    }
}
class Son extends Father{
    public void test(){
        System.out.println("子类的test()方法");
    }
    
    public void method(){
        System.out.println("子类的method()方法");
    }
}
View Code
public class TestSuper {
    public static void main(String[] args) {
        ErZi r = new ErZi();
    }
}
class ZuZong{
    public void test(){
        System.out.println("祖宗的test()");
    }
}
class YeYe extends ZuZong{
    
}
class BaBa extends YeYe{
    public void test(){
        System.out.println("爸爸的test()");
    }
}
class ErZi extends BaBa{
    ErZi(){
        super.test();
    }
    
    public void test(){
        System.out.println("儿子test()");
    }
}
View Code
public class TestThis {

}
class Base{
    public void test(){
        System.out.println("父类的test()");
    }
    public void funtion(){
        System.out.println("父类的funtion()");
    }
}
class Sub extends Base{
    public void method(){
        System.out.println("子类的method()");
        //this.test();
        //this对象它有从Base继承的test()和Sub类自己声明的method()
        
        test();//省略的this.
        
        this.function();//代表子类自己的
    }
    
    //重写
    public void function(){
        System.out.println("子类的funtion()");
    }
}
View Code

 

3、多态(Polymorphism)

多种形态,目的是使代码更灵活,功能更丰富。

 * 如何理解它? 针对方法
 * 1方法的重载(一个类中一个方法功能的多种表现形式)与重写(父子类对于同一个方法表现出不同的形式):一个功能有多种形式
 * public static int getMax(int x, int y)
 * public static int getMax(int x, int y,int z)
 * public static double getMax(double x, double z)
 * 功能都是找最大值,它有多种形式

 * public class Employee{
 *     public String getInfo(){
 *         ....
 *     }
 * }
 * public class Manager extends Employee{
 *     public String getInfo(){
 *         .... + 奖金
 *     }
 * }
 * 父子类中该方法都是返回对象的详细信息,但是父子类中有两种形式
 * 
 * 2、对象的多态性
 *     某个对象在Java中 1)编译时类型(编辑代码且javac.exe); 2)运行时类型,不一致(java.exe)  --->>多态 父类的引用指向子类的对象
 **  前提条件:
 *  (1)类有继承关系
 *  (2方法的重写
 *  (3)多态引用:父类的变量指向子类的对象 
 *  Person p = new Man();
 *  出现的对象多态性的现象:
 *      编译时按照父类的类型编译,运行时按照子类的类型运行,执行的方法是子类重写父类的方法体。
 * 
 */属性没有多态,多态是针对方法的。。

 

public class Exam2 {
    public static void main(String[] args) {
        Base b = new Sub();      //多态引用;
        System.out.println(b.x); //1   -->属性没有多态,按照编译时,b对象有1个x,是Base类中的。
    }
}
class Base{              
    int x = 1;
}
class Sub extends Base{
    int x = 2;
}
public class TestPolymorphism {

    public static void main(String[] args) {
        Person p = new Man(); //编译时类型是Person,运行时类型是Man
        p.eat();  //运行时候是Man类型; 
        //p.smoke();//编译时报错,因为p在编译时按照Person类型,Person类型中没有smoke()方法
        
        System.out.println(p.name);
        p.sleep();
        
        
        //Man m =  (Man) new Person(); //发生异常java.lang.ClassCastException

        Person pe = new Man(); //前提父类原先指向的就是本身对象才能向下转型成功,
                                //如果父类原先指向的是其他类型(父类的,兄弟类的)的对象
        Man m1 = (Man)pe;
        m1.smoke();
        System.out.println(m1.name);
        
    }
}
class Person{
    String name = "父类";
    int age = 100;
    
    public void eat(){
        System.out.println("吃吃吃饭");
        }
    public void sleep(){
        System.out.println("睡觉觉");
    }
}
        
class Man extends Person{ 
    String name = "子类男人";
    int age = 20;
    
    /*public void eat(){
        System.out.println("狼吞虎咽");
        }*/
    public void sleep(){
        System.out.println("呼呼大睡");
    }
    public void smoke(){
        System.out.println("吞云吐雾");
    }
}

class Son extends Man{

    @Override
    public void eat() {
        System.out.println("儿子吃");
    }

    @Override
    public void sleep() {
        System.out.println("儿子睡觉");
    }

    @Override
    public void smoke() {
        System.out.println("儿子吸烟");
    }

}
View Code

 

public class TestPolymorphism {
    public static void main(String[] args) {
//        Object obj = new String("hello");
//        此时的obj对象就有两种类型,编译时类型是Object类型,运行时是String类型
    }
}
//本态引用:编译时类型和运行时类型是一样的  ;  VS  多态引用:父类的变量指向子类的对象 如 Person p = new Man(); 
//        Person p = new Person(); 
//        Man m = new Man();
//        Girl g = new Girl();

 

* 多态的第一个应用:多态数组
 * 元素的类型是父类的类型,元素存的是子类的对象
 * 
 * 需求:用一个数组来存储多个图形对象,这里面可能有圆对象,可能有矩形对象,统一管理他们,要显示他们的面积、周长、向下信息等,甚至按照面积排序...
 * 1、声明一个Circle类,有半径radius,有求面积的方法,求周长的方法,返回详细信息的方法
 * 2、声明一个Rectangle矩形类,有长和宽,有求面积的方法,求周长的方法,返回详细信息的方法
 * 3、声明一个父类Graphic图形类,让Circle和Rectangle继承它
 * 
 * 类:一类具有相同特性的事物的抽象描述。

 

public class TestUse1 {

    public static void main(String[] args) {
        
        //多态引用,左边的arr[]是父类的类型Graphic,右边赋值的是子类的对象;
        Graphic[] arr = new Graphic[3]; //数组的声明和初始化
        arr[0] = new Circle(2);
        arr[1] = new Ractangle(3,2);
        arr[2] = new Circle(6.1);
        
        //编译时g按照Graphic的父类类型编译,执行的时候,执行的是子类重写的方法体
        for(Graphic num: arr){
            System.out.println(num.getInfo() + "\n");
            //System.out.println(num.getArea() + "\t");
        }

    }
}

 

public class TestUse1 {

    public static void main(String[] args) {
        
        //多态引用,左边的arr[]是父类的类型Graphic,右边赋值的是子类的对象;
        Graphic[] arr = new Graphic[3]; //数组的声明和初始化
        arr[0] = new Circle(2);
        arr[1] = new Ractangle(3,2);
        arr[2] = new Circle(6.1);
        
        //编译时g按照Graphic的父类类型编译,执行的时候,执行的是子类重写的方法体
        for(Graphic num: arr){
            System.out.println(num.getInfo() + "\n");
            //System.out.println(num.getArea() + "\t");
        }

    }
}


package com.atguigu.variable;

public class Graphic {
    public double getArea(){
        return 0.0;
    }
    public double getPrimeter(){
        return 0.0;
    }
    public String getInfo(){
        return "";
    }
}


package com.atguigu.variable;

public class Circle extends Graphic{
    private double radius;

    public Circle() {
        super();
    }

    public Circle(double radius) {
        super();
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    public double getArea(){
        return Math.PI * radius * radius;
    }
    public double getPrimeter(){
        return 2 * Math.PI * radius;
    }
    public String getInfo(){
        return "半径" + radius + "\t面积" + getArea() + "\t周长" + getPrimeter(); 
    }
    
}


package com.atguigu.variable;

public class Ractangle extends Graphic{
    private double length;
    private double width;
    public Ractangle() {
        super();
    }
    public Ractangle(double length, double width) {
        super();
        this.length = length;
        this.width = width;
    }
    public double getLength() {
        return length;
    }
    public void setLength(double length) {
        this.length = length;
    }
    public double getWidth() {
        return width;
    }
    public void setWidth(double width) {
        this.width = width;
    }
    
    public double getArea(){
        return length * width;
    }
    public double getPrimeter(){
        return 2 * (length + width);
    }
    public String getInfo(){
        return "长" + length +"\t宽:"+ width + "\t面积" + getArea() + "\t周长" + getPrimeter(); 
    }
}
View Code

 

* 多态的应用之二:多态参数
 * 形参是:父类类型
 * 实参是:子类的对象
 * 
 * 需求:
 * 1、声明一个Circle类,有半径radius,有求面积的方法
 * 2、声明一个Rectangle矩形类,有长和宽,有求面积的方法
 * 3、声明一个Triangle三角形,有三边,有求面积的方法
 * 3、在测试类中,声明一个方法,功能:可以比较任意两个图形对象的面积,是否相等
public class TestUse2 {

    public static void main(String[] args) {
        Circle c = new Circle(2);
        Ractangle r = new Ractangle(3, 2);
        //实参给形参赋值
        //形参的类型:Graphic,实参的类型:一个是Circle,一个是Rectangle
        //隐含了:Graphic g1 = c;//多态引用
        //隐含了:Graphic g2 = r;//多态引用
        boolean result = equalsGraphic(c, r);
        if(result){
            System.out.println("面积相等");
        }else{
            System.out.println("面积不相等");
        }

    }
    //可以比较任意两个图形对象的面积,是否相等
    //返回值类型:boolean
    //形参?两个图形类型
    public static boolean equalsGraphic(Graphic g1, Graphic g2){
        //g1和g2编译时按照Graphic类型,运行时g1按照Circle,g2按照Rectangle类型
        if(g1.getArea() == g2.getArea()){
            return true;
        }else{
            return false;
        }
    }
    
}

 向上转型和向下转型

* 基本数据类型:byte,short,int,long,float,double,boolean,char
 * 1、自动类型转换
 * byte,short,char->int->long->float->double
 * boolean不参与
 * ....
 * 
 * 2、强制类型转换
 * double->float->long->int->char,byte,short
 * 强制类型转换需要(),可能损失精度或溢出
 * boolean不参与
 * ....
 * 
 * 父子类之间类型转换:
1、向上转型:自动完成 子类 转 父类 * Person p = new Man();//一个Man的对象在编译期间向上转型为Person类型
    一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,即向上转型;那么
        该变量就不能再访问子类中添加的属性和方法只能访问子类中重写父类的方法 和 父类中的属性、方法(子类中没有父类中有的方法也可以调用);
重写形参要一模一样,如果形参不一样,要看这个参数是否符合(父类的引用变量可以接收子类的对象)见习题。
如果没有重写,只是本态引用,则子类的变量只能执行它自己和继承到父类的方法。
 *  Graphic[] arr = new Graphic[5];
 *  arr[0] = new Circle(2);//一个Circle对象在编译期间向上转型为Graphic类型
 *  
2、向下转型:强制完成
      Man m =  (Man) new Person(); //发生异常java.lang.ClassCastException
  
Person pe = new Man(); //前提父类原先指向的就是本身对象才能向下转型成功,如果父类原先指向的是其他类型(父类的,兄弟类的)的对象,那么就会发生java.lang.ClassCastException类型转换异常
Man m1 = (Man)pe;
m1.smoke(); //对象m1可以调用子类的所有方法和属性;不能调用父类的属性和方法。
System.out.println(m1.name);
*    Person[] arr = new Person[5];
    arr[0] = new Man();//向上转型
    Man m = (Man) arr[0];//向下转型     --->>   Man m = (Man) new Person();这样子写它会发生异常的。      
 *
 *    向下转型之前,一定发生过向上转型。
 *
 * 
 * 父类的变量中可以存储子类的对象,
 * 但是子类的变量中是不能存储父类的对象。
* 关键字:instanceof * 为了避免转型的失败,可以在转型之前加instancof判断

 

public class TestClassCast {

    public static void main(String[] args) {
        Person[] arr = new Person[3];
        arr[0] = new Man();
        arr[1] = new Girl();
        arr[2] = new Person();
        
        //希望调用Man类型的smoke()方法
        //arr[0].smoke(); //编译时,arr[0]是按照父类Person类型编译的
        Man m = (Man) arr[0]; //向下转型;
        m.smoke();
        
        //编译没保错,因为编译时,arr[1]按照父类Person类型编译的,从Person类型向下转为Man,语法上可以
        //Man m2 = (Man)arr[1];//运行时,发生java.lang.ClassCastException:类型转换异常
        //m2.smoke();           //.Girl cannot be cast to .Man
        
        Man m3 = (Man)arr[2];//运行时,发生java.lang.ClassCastException:类型转换异常
        m3.smoke();        //.Person cannot be cast to .Man 
    }

}

Man m  -->>指向Man对象是可以的

Man m2 --->>不能指向Girl对象,因为它俩之间没有继承关系的。

Man m3 ---->>不能指向Person父对象。

 

public class TestClassCast {

    public static void main(String[] args) {
        Person[] arr = new Person[3];
        arr[0] = new Man();
        arr[1] = new Girl();
        arr[2] = new Person();
        for(Person p : arr){
            p.eat();
            //p.sleep();
            if(p instanceof Man){
                Man m =(Man) p;
                m.smoke();
            }if(p instanceof Girl){
                Girl g = (Girl) p;
                g.shopping();
            }
        }
    }
}

 

Object

public class TestObject {
    public static void main(String[] args) {
        Circle circle = new Circle();
        int[] arr = new int[]{1,2,3,4};
        
        //多态引用
        Object obj = new Circle();
        Object obj2 = new int[4];
        
        Object[] all = new Object[10];
    }
}
class Circle{
    
}

 

* java.lang.Object:
 *     类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。 
 * 
 * 潜台词:
 * (1)所有类都是Object的子类
 *       一个类如果没有显式声明它继承某个别的类,那么它的直接父类就是Object。
 * (2)Object中的所有的方法、属性,在所有类中都有
 *  即任意对象(包括数组对象),都可以调用Object中的方法。
 * (3)所有对象在创建时,都会调用Object的无参构造方法。  看不出来
 * (4)Object类型的变量,可以接受任意类型的数据
 *  即Object类型的变量可以与非Object对象构成多态引用。
 *  即Object类型的数组可以存储所有类型的数据。
 *  
 *  
 *  常用方法:
* 常用方法:
 * (1)public final Class<?> getClass():返回此 Object 的运行时类。
 * (2)public String toString():返回该对象的字符串表示。
 * Object类中的toString():由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成
 * 一个对象如果直接用Sysout打印 或 与字符串拼接,默认就是调用这个对象的toString()方法
 * 
 * 建议所有子类重写toString()方法,使得对象的信息更清晰。
 * (3)protected void finalize()throws Throwable
 * 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写 finalize 方法,以配置系统资源或执行其他清除。
 * (A)这个finalize()方法,由垃圾回收器调用,不是程序员手动调用
 * (B)什么时候调用?当垃圾回收器确定某个对象是“垃圾”,才会调用,而且这个方法每个对象只会被调用一次。
 *         更深的意思:如果这个对象的finalize()方法中,使得这个对象“复活”了,那么下次它再称为垃圾时,就不会调用finalize()
 * 
 * (4)public int hashCode():返回该对象的哈希码值。(就是每一个对象有一个数字编码,这个编码表示这个对象的信息)
 *     理想状态下:每一个对象的hash码是唯一的。
 *  现实中,可能存在两个对象的hash码是相同的。
 *  hash码是由散列函数根据对象的信息计算出来的。
 *  
 *  记住结论:
 *      如果两个对象的hash码“不相等”,那么这个两个对象一定“不是同一个对象”;
 *      但是如果两个对象的hash码“相等”,那么这个两个对象却不一定是“相等的对象”,可能是相同,也可能不同。
 *  即不同通过hash码来确定两个对象是否“相等”
 *  
 *  支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。 
 *  
 * (5)public boolean equals(Object obj)
 *     它是真正用来确定两个对象是否“相等”的唯一标准。
 *  
 *  如果某个类没有重写equals方法,那么Object中的equals方法和“==”是一样,比较的是对象的内存地址。
 *  注意:String类型是重写过的equals方法,因此String类型的比较不用==,用equals
String类是final修饰的。
==和equals()的区别

==:如果是基本数据类型,比较的是数据值
如果是引用数据类型,比较的是对象的地址值
equals():必须是对象才能调用,它是Object类中声明的,如果子类没有重写,那么它的效果和==是一样的,也是比较对象的地址。
如果子类重写了,那么就按照重写的规则进行比较,例如String类型重写了equals,比较的是字符串的字符内容。
String str = new String();  //TestFactory与String类发生耦合,依赖
        //对象的使用者TestFactory不负责BMW和Aodi对象的创建,与BMW和Aodi类解耦合
        Car b = Factory.getCar("宝马");
        b.run();
        Car a = Factory.getCar("奥迪");
        a.run();

class Factory{
    //工厂只负责创建对象;创建一个getCar方法来生产Car的对象
    public static Car getCar(String type){
        if("宝马".equals(type)){
            return new BMW();
        }else if("奥迪".equals(type)){
            return new AoDi();
        }
        return null;
        
    }
}

 重写一个类的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

 

原理:

public class TestObjectMethod {

    public static void main(String[] args) {
        
        Object obj = "Hello";
        System.out.println(obj.getClass());//class java.lang.String
        Student stu = new Student(1, "kris");
        //com.atguigu.object.Student@15db9742
        //由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成
        
        System.out.println(stu.toString());
        System.out.println(stu);
        
        String info = "学生信息" + stu;
        System.out.println(info);
        
        
        //字符串对象
        System.out.println("Aaa".hashCode()); //65569
        
        
        Student s1 = new Student(1,"alex");
        Student s2 = new Student(1,"alex");
        System.out.println(s1 == s2); //false
        System.out.println(s1.equals(s2)); //true
        
        System.out.println(s1.equals(s1));//true地址相同,同一个对象,自己和自己比较
        System.out.println(s1.equals("hello"));//false s1的运行时是Student,"hello"的运行时类型String
        
        
    }


}

class Student{
    
    private int id;
    private String name;
    public Student() {
        super();
    }
    public Student(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + "]";
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    

    //System.out.println(s1.equals(s2));
    //s1是this,调用equals的对象,就是this
    //s2是实参,给obj形参赋值
    @Override
    public boolean equals(Object obj) {
        //如果s1和s2的地址是一样,就返回true
        if (this == obj)
            return true;
        
        //obj为null,就返回false,因为this现在一定不是null,因为如果this是null早就空指针异常了,进不来
        if (obj == null)
            return false;
        
        //this的运行时类型和obj的运行时类型是否一样
        if (getClass() != obj.getClass())
            return false;
        
        //this和obj的运行时类型是一样,那么obj也是Student对象
/*        if(this.id != obj.id){//因为obj的编译时类型是Object
            
        }*/
        
        Student other = (Student) obj;
        if (id != other.id)
            return false;
        
        //学号是一样
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))//这个equals是String,因为name是String类型
            return false;
        return true;
    }
    
    
}
View Code

finalize()方法,由垃圾回收器调用;原理如下

* Alt +Shift +S,选择Override....
 */

//这个finalize()方法,由垃圾回收器调用
public class TestFinalize {
    public static void main(String[] args) {
        for (int i = 1; i <=10; i++) {
            //my是属于for循环的循环体的局部变量
            //每一次循环,这个my都是全新的my,意味着上一次的my指向的对象,就称为了垃圾
            MyClass my = new MyClass();
            System.out.println("my = " + my);
        }
        
        //gc:Garbage Collector
        System.gc();//通知垃圾回收器来回收垃圾
        
        try {
            //让程序慢点退出,1秒后再退出
            Thread.sleep(1000);//单位是毫秒,1s = 1000ms
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class MyClass{

    @Override
    protected void finalize() throws Throwable {
        System.out.println("轻轻的我走了,正如我轻轻的来..");
    }
    
}
View Code

 

抽象类

* 抽象类:
 *     当某个父类,需要体现它的子类们的共同的特性,例如,共同的方法,但是这个方法,在父类中无法给出合理的实现,
 *  具体的实现应该有子类来完成。那么,这样的方法我们可以设计为抽象方法。包含抽象方法的类,必须是抽象类。
 *  
 * 一、抽象方法
 * 【修饰符】 abstract 返回值类型  方法名(【形参列表】);
 * 
 * 注意:
 * (1抽象方法必须有abstract修饰?????????????
 * (2抽象方法没有方法体{}
 * (3抽象方法必须在抽象类、接口中
 * 换句话说,如果某个类包含抽象方法,这个类必须是一个抽象类。
 * 
 * 二、抽象类
 * 【权限修饰符】  abstract class 抽象类名{
 * }
 * 
 * 抽象类的特点:
 * (1)抽象类不能实例化,即抽象类不能直接创建对象
 * (2抽象类就是用来被继承的,子类继承抽象类时,必须重写(实现)抽象方法,否则这个子类也得是一个抽象类
 * (3)包含抽象方法的类,必须是抽象类,但是反过来,抽象类可以没有抽象方法。
 *     如果一个抽象类,没有抽象方法,这样的设计的目的,就是为了不让你创建它的对象,让你创建它的子类的对象。
 * 
 * 
 * 抽象类的成员?
 * (1)属性
 * (2)非抽象方法
 * (3)代码块:给属性初始化
 * (4)构造器:给子类调用
 * 
 * 比较:普通类与抽象类的区别
 * (1)抽象类有abstract修饰,普通类没有
 * (2)抽象类可以有抽象方法,普通类不能有
 * (3)抽象类不能实例化,普通类可以
 * 

 

public class TestAbstract {
    public static void main(String[] args) {
//        Graphic g = new Graphic();//不能创建抽象类对象
        
        Graphic g1 = new Circle(1.2);//多态引用,创建的是子类的对象
        System.out.println("面积:" + g1.getArea());
    }
}
abstract class Graphic{
    private static String info;
    private String name;
    
    public Graphic() {
        super();
    }
    public Graphic(String name) {
        super();
        this.name = name;
    }
    public static String getInfo() {
        return info;
    }
    public static void setInfo(String info) {
        Graphic.info = info;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public abstract double getArea();
}
class Circle extends Graphic{
    private double radius;
    
    public Circle(double radius) {
        super();
        this.radius = radius;
    }

    public double getArea(){
        return Math.PI * radius * radius;
    }
}
class Rectangle extends Graphic{
    private double length;
    private double width;

    @Override
    public double getArea() {
        return length * width;
    }
    
}

abstract class Person{
    
}
class Man extends Person{
    
}
View Code

 

设计模式

* 设计模式:解决某些问题,形成的代码的套路。
 * 常见的设计模式一共有23种。我们在SE阶段会介绍一小部分,但是因为设计模式比较难理解,对于初级程序员来说,特别难。
 * SE阶段,把设计模式的要求定为了解。除了一个设计模式需要掌握,并且能够百分百手写的,是单例。 
 * 
 * 抽象类有一个应用:模板设计模式(了解,认识)
 * 
 * 当解决某个问题时,它的整体的算法结构,步骤是确定的,只是其中的某个小步骤是不确定的,由使用者来确定。
 * 遇到这种情况,就需要用到模板设计模式。
 * 我们把能确定部分先完成,不能确定的部分,抽象成一个抽象方法,让使用者去实现它。
 * 
 * 例如:要你写一个功能,它可以计算任意一段代码的运行时间
 * 算法,步骤:
 * 1、先获取开始时间
 * 2、执行xx代码
 * 3、获取结束时间
 * 4、计算时间差
 * 这里时间用毫秒表示,用long类型
 * 
 * 提示:
 *     System.currentTimeMillis():可以返回当前系统时间距离1970年1月1日 0点0分0秒0毫秒的毫秒值
 */
public class TestTemplate {
    public static void main(String[] args) {
        MyCalTime my = new MyCalTime();
        long time = my.getTime();
        
        System.out.println("运行时间:" + time);
    }
}

//这个类就是模板类
abstract class CalTime{
    //如果不希望子类重写getTime()方法,修改算法结构,可以用final修饰
    public final long getTime(){
        long start = System.currentTimeMillis();
        
        doWork();
        
        long end = System.currentTimeMillis();
        
        return end - start;
    }
    
    public abstract void doWork();
}

//我需要计算从1加到100000的和以及运行时间
class MyCalTime extends CalTime{

    @Override
    public void doWork() {
        long sum = 0;
         for (int i = 1; i <= 100000;  i++) {
            sum += i;
        }
        System.out.println("和:" + sum);
    }
    
}

//我需要计算复制一个文件的时间
class FileCopyCalTime extends CalTime{

    @Override
    public void doWork() {
        //复制文件功能
    }
    
}

 

posted @ 2018-11-25 17:16  kris12  阅读(508)  评论(0编辑  收藏  举报
levels of contents