博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaSE| 面向对象的三大特征
阅读量:4946 次
发布时间:2019-06-11

本文共 27689 字,大约阅读时间需要 92 分钟。

 

 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的动态绑定技术(),所以对象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 : 100sub : 100base : 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() {        //复制文件功能    }    }

 

转载于:https://www.cnblogs.com/shengyang17/p/10010418.html

你可能感兴趣的文章
关于响应式布局
查看>>
详解ASP.Net 4中的aspnet_regsql.exe
查看>>
python 多进程和多线程的区别
查看>>
hdu1398
查看>>
[android] 网络断开的监听
查看>>
156.Binary Tree Upside Down
查看>>
MongoDB在windows下安装配置
查看>>
Upselling promotion stored procedure
查看>>
mysql编码配置
查看>>
KVM地址翻译流程及EPT页表的建立过程
查看>>
sigar
查看>>
iOS7自定义statusbar和navigationbar的若干问题
查看>>
c++ 网络编程(一)TCP/UDP windows/linux 下入门级socket通信 客户端与服务端交互代码...
查看>>
程序员如何提高影响力:手把手教你塑造个人品牌
查看>>
身份证校验原理和PHP实现
查看>>
[Locked] Wiggle Sort
查看>>
deque
查看>>
计算机
查看>>
Ext JS学习第十三天 Ext基础之 Ext.Element
查看>>
python--迭代器与生成器
查看>>