4、面向对象(二)

1、多态

多态简介

多态就是事物存在的多种形态,比如你在大街上看见一只藏獒,你可以说这只藏獒真凶猛,也可以说这只狗真凶猛,还可以说这个动物真凶猛,以上三种说法其实都是指的这只藏獒。
在Java里面,也是存在多态的,只要全部符合下面这三种情况,就是多态

  • 有继承
  • 有方法重写
  • 有父类引用指向子类对象

例如下面代码就构成多态

class Animal{
    public int num = 10;
    
    public void eat(){
        System.out.println("动物再吃!");
    }
}

class Cat extends Animal{
    public int num = 20;
    public void eat(){
        System.out.println("猫再吃!");
    }
}

class Animal_Text{
    public static void main(String[] args){
        Animal m1 = new Cat();// 父亲引用指向子类对象
        m1.eat(); // 猫在吃 动态绑定和静态绑定
        System.out.println(m1.num); // 10 说明成员变量不存在
    
    }
}

静态绑定和动态绑定

上面代码中,a1是Animal类型的一个引用,指向的是其子类Cat的对象,这个就叫做父类引用指向子类对象。程序在编译的时候a1被看做Animal类型,所以a1.eat()绑定的是Animal类中的eat()方法,这叫做静态绑定,程序运行时,a1指向的是堆中的Cat对象,而在Cat中对eat()方法进行了重写,所以在运行阶段绑定的是Cat中的eat()方法,这叫做动态绑定

强制类型转换

上面代码中子类向父类型进行转换,是自动类型转换,也叫做向上转型。还有一种情况是父类向子类型转换,是强制类型转换,也叫向下转型。下面的代码演示了强制类型转换

class Animal{
    public int num = 10;
    
    public void eat(){
        System.out.println("动物再吃!");
    }
}

class Cat extends Animal{
    public int num = 20;
    public void eat(){
        System.out.println("猫再吃!");
    }
    
    //Cat特有的方法.
    public void move(){
        System.out.println("猫走路很轻盈!");
    }
}


class Dog extends Animal{

    //重写
    public void eat(){
        System.out.println("狗啃骨头!");
    }
}

class Animal_Text{
    public static void main(String[] args){
        Animal a1 = new Cat();// 父亲引用指向子类对象
        //如果要是想执行Cat里面的move方法该怎么办?
        //只能强制类型转换,需要加强制类型转换符
        Cat c1 = (Cat)a1;
        c1.move();

        Animal a2 = new Dog(); //向上转型.
        //强制类型转换
        //Cat c2 = (Cat)a2; //会报错 java.lang.ClassCastException
        
    
    }
}

instanceof关键字

上面的代码里面将一个指向Dog对象的Animal引用a2进行强制转换成Cat类型时报出了ClassCastException类转型错误,开发中要是想避免这种错误需要使用instanceof来判断一下。

class Animal{
    public int num = 10;
    
    public void eat(){
        System.out.println("动物再吃!");
    }
}

class Cat extends Animal{
    public int num = 20;
    public void eat(){
        System.out.println("猫再吃!");
    }
    
    //Cat特有的方法.
    public void move(){
        System.out.println("猫走路很轻盈!");
    }
}


class Dog extends Animal{

    //重写
    public void eat(){
        System.out.println("狗啃骨头!");
    }
}

class Animal_Text{
    public static void main(String[] args){
        Animal a1 = new Cat();// 父亲引用指向子类对象
        //如果要是想执行Cat里面的move方法该怎么办?
        //只能强制类型转换,需要加强制类型转换符
        Cat c1 = (Cat)a1;
        c1.move();

        Animal a2 = new Dog(); //向上转型.
        //进行强制类型转换时,需要先使用instanceof进行判断,避免ClassCastException
        if (a2 instanceof Cat){
            //强制类型转换
            Cat c2 = (Cat)a2;
        }else{
            System.out.println("无法进行强制类型转换");
        }
    }
}

多态的优点

  • 提高了程序的扩展性
  • 降低了代码之间的耦合
class Car{
    public void run(){
        System.out.println("汽车在行驶!");
    }
}

class Benz extends Car{
    public void run(){
        System.out.println("奔驰汽车在跑");
    }
}

class BWM extends Car{
    public void run(){
        System.out.println("宝马汽车再跑!");
    }
}

class Person{
     /*
    public void drive(Benz bc){
        bc.run();
    }
    奔驰汽车坏了,再重新创建一个开宝马汽车的方法
    public void drive(BMW bm){
        bm.run();
    }
    */

    //上面代码扩展性太差,每新增加一种品牌的汽车就需要再写一个方法
    //将参数修改为Car类型,这样不论增加什么样的品牌汽车,都可以调用这个方法
    public void driver(Car c){
        c.run();
    }
}

public class Test{
    public static void main(String[] args){
        Person james = new Person();

        //Benz bc = new Benz();
        //james.drive(bc);

        BMW bm = new BMW();    
        james.drive(bm);
    
    }
}

2、final关键字

final的特点

final的中文意思是最终,既然是最终就是已经结束了,无法再改变了。在Java里面final关键字同样也有着类似的功能。

  • final修饰的类无法被继承。
  • final修饰的方法无法被重写。
  • final修饰的局部变量,一旦赋值,不可再改变。
  • final修饰的成员变量必须初始化值。
public class Final{
    public static void main(String[] name){
        
    }
}

// final修饰的类无法被继承。
final class A1{

}
class A2 extends A1{

}



//final修饰的方法无法被重写。
class B1{
    public final void method(){
    
    }
}
class B2 extends B1{
    public void method(){
    
    }
}



//final修饰的局部变量,一旦赋值,不可再改变。
class C1{

    public void C2(){
        final int x = 1;
        x = 2;
    
    }
}



//final修饰的成员变量必须初始化值。
class D{
    //final修饰的成员变量必须手动初始化.
    final int D1 = 100; 

    //final修饰的成员变量一般和static联用。
    ////java规范中要求所有的常量"大写"
    final static int D2 = 200;
}

final修饰引用类型

final修饰的引用类型,该引用不可再重新指向其他的java对象。但是fianl修饰的引用,该引用指向的对象的属性值是可以修改的。

  • 基本类型,是值不能被改变
  • 引用类型,是地址值不能被改变,对象中的属性可以改变
public class FinalTest01{
    public static void main(String[] args){
    final Customer c = new Customer("张三",20);
    //c是final的,无法重新赋值。
    //c = new Customer("李四",21);//Error
    c.name = "王五";
    c.age = 25;
    
    System.out.println(c.name);
    System.out.println(c.age);
    
    }
}

class Customer{
    String name;
    int age;
    Customer(String name,int age){
        this.name = name;
        this.age  = age;
    
    }
}

3、抽象类

抽象的概念

抽象这个词说白了就是看不懂,毕加索的画一般都是被称为抽象的。在java里面可以使用关键字abstract修饰一个类,这样的类被称为抽象类,abstract修饰的方法叫做抽象方法。抽象类或抽象方法一般也是看不懂的,因为里面可能根本就没有代码。

抽象类的特点

  • 抽象类无法被实例化,无法创建抽象类的对象。
  • 虽然抽象类没有办法实例化,但是抽象类也有构造方法,该构造方法是给子类创建对象用的。这也算是多态的一种。
  • 抽象类中不一定有抽象方法,但抽象方法必须出现在抽象类中。
  • 抽象类中的子类可以是抽象类,如果不是抽象类的话必须对抽象类中的抽象方法进行重写。
  • 抽象类和抽象方法不能被final修饰
public abstract class A{

    //构造方法
    A(){
        System.out.println("A....");
    }

    // 抽象方法
    public abstract void m1();

    public static void main(String[] args){
        //抽象类无法创建对象
        // A a = new A();

        //多态
        A a = new B();
    }
}

class B extends A{
    public void m1(){
    
    }

    B(){
        super(); //父类的构造方法虽然调用了,但是并没有创建父类对象。
        System.out.println("B....");
    }
}

4、接口

接口的概述

电脑上面的主板有很多接口,比如内存条的接口,有了这个接口,可以插入多个内存条,主板和内存条可能不是同一家生产厂商,但是两种物体却能结合到一起,正是因为这个接口的存在。只要厂家遵循这个接口,主板和内存条就可以随意更换,提高了可插拔性,接口其实也是体现着一种规范。
在java语言里面使用interface来声明一个接口,接口其实是一个特殊的抽象类,在接口里面的方法全部都是抽象的。
关于接口,有几个需要注意的地方:

    • 接口中只能出现常量和抽象方法
    • 接口里面没有构造方法,无法创建接口的对象
    • 接口和接口之间支持多继承,即一个接口可以有多个父接口
    • 一个类可以实现多个接口,即一个类可以有多个父接口
    • 一个类如果实现了接口,那么这个类需要重写接口中所有的抽象方法(建议),如果不重写则这个类需要声明为抽象类(不建议)
public interface A{

    //常量(必须用public static final修饰)
    public static final double PI = 3.14;

    //public static final是可以省略的.
    //double PI = 3.14;


    //抽象方法(接口中所有的抽象方法都是public abstract)
    public abstract void m1();

    //public abstract是可以省略的.
    void m2();

}

interface B{
    void m1();
}

interface C{
    void m2();
}

interface D{
    void m3();
}

interface E extends B,C,D{
    void m4();
}

//implements是实现的意思,是一个关键字.
//implements和extends意义相同。
class MyClass implements B,C{
    public void m1(){}
    public void m2(){}
}

接口的作用

  • 可以使项目分层,都面向接口开发,提高开发效率
  • 降低了代码之间的耦合度,提高了代码的可插拔性

开发中尽量使用接口,少用抽象类,一个类可以实现多个接口,却只能继承一个父类

将之前的james开汽车的例子修改一下

将Car定义为接口

interface Car {
    public void run();
}

创建Benz和BMW类去实现这个接口

class Benz implements Car {
    public void run(){
        System.out.println("奔驰汽车在跑");
    }
}

class BMW implements Car {
    public void run(){
        System.out.println("宝马汽车在跑");
    }
}

Person类不变

class Person {
    public void drive(Car c){
        c.run();
    }
}

测试类不变

public class Test01 {
    public static void main(String[] args) {
        Person james = new Person();
        Benz bc = new Benz();
        //james.drive(bc);
        //BMW bm = new BMW();
        james.drive(bc);
    }
}

5、Object类之equals方法

equals方法

Java对象中的equals方法的设计目的:判断两个对象是否一样。
Java源码里面Object中的equals方法:

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

== 两边如果是引用类型,则比较内存地址,地址相同则是true,反之则false.

Object中的equals方法比较的是两个引用的内存地址。但是在现实的业务逻辑当中,不应该比较内存地址,应该比较地址里面的内容,所以需要对equals方法进行重写。

注意:在使用自己创建的类进行equals比较时,一定要先重写equals方法

class Car{

    int id;
    String name;

    public Star(int id,String name){
        this.id = id;
        this.name = name;
    }


//根据需求规定重写equals方法
    public boolean equals(Object obj){
        if (this == obj){
            return true;
        }
        if (obj instanceof Star){
            Star s = (Star)obj;
            if(s.id == id && s.name.equals(name)){
                return true;
            }
        }
        return false;
    }

}


public class Test01{
    public static void main(String[] args){
        Object o1 = new Object();
        Object o2 = new Object();

        boolean b1 = o1.equals(o2);

        System.out.println(b1);

        Star s1 = new Star(100,"成龙");
        Star s2 = new Star(100,"成龙");
        Star s3 = new Star(100,"李连杰");

        System.out.println(s1.equals(s2));
        System.out.println(s2.equals(s3));
    
    }
}

比较两个String类型时,不能使用==,要使用equals方法,String已经重写了Object中的equals方法,比较的是内容。

public class Test02{

    public static void main(String[] args){

        String s1 = new String("ABC");
        String s2 = new String("ABC");

        System.out.println(s1==s2); //false

        //String已经重写了Object中的equals方法,比较的是内容。
        System.out.println(s1.equals(s2)); //true

    }
}

6、finalize方法

finalize方法

finalize方法不需要程序员去调用,由系统自动调用。java对象如果没有更多的引用指向它,则该java对象成为垃圾数据,等待垃圾回收器的回收,垃圾回收器在回收这个java对象之前会自动调用该对象的finalize方法。finalize方法是该对象马上就要被回收了,例如:需要释放资源,则可以在该方法中释放。

public class Test01{

    public static void main(String[] args){

        Person p1 = new Person();

        p1 = null; //没有引用再指向它.等待回收.

        //程序员只能“建议”垃圾回收器回收垃圾.
        System.gc();
    }

}


class Person{

    //重写Object中的finalize方法.
    public void finalize() throws Throwable { 

        System.out.println(this + "马上就要被回收了!");

        //让引用再次重新指向该对象,该对象不是垃圾数据,不会被垃圾回收器回收!
        //Person p = this;
    }
}

7、package(包)和import

在日常生活中有很多同名的人,为了将这些同名的人进行区分,就出现了身份证,每个人的身份证号都是不一样的。在Java语言里面,开发者难免会编写出同名的类,为了区分出不通人开发出来的类,Java引入了包的概念。

使用package声明包名

在类名前面使用关键字package加入包名来避免命名冲突问题,因为域名是世界上唯一的,所以建议使用公司倒写的域名来命名包名,通常是小写
例如:package com.monkey1024.score.system
上面包名的含义是monkey1024公司开发的score项目(学生成绩管理项目),system是score项目里面的一个模块。
假设这个score项目里面有学生模块、老师模块,可以这样进行命名:
学生模块:com.monkey1024.score.student
在学生模块的包里面,可以放置一些学生相关的类,比如AddStudent.class、DeleteSudent.class
老师模块:com.monkey1024.score.teacher
在老师模块的包里面,可以放置一些老师相关的类,比如AddTeacher.class、DeleteTeacher.class
其实这个包名就是文件夹的名称,如果按照上述命名,假设在我存放在电脑的f盘里面,F:commonkey1024scorestudentAddStudent.class

注意:

  • package语句只能出现在.java源文件的第一行
  • package语句在一个java文件中只能有一个
  • 如果没有package,默认表示无包名
package com.monkey1024.oa.student;

public class AddStudent{
    public void add(){
        System.out.println("添加学生");
    }
}    

package com.monkey1024.oa.student;
public class Test01{

    public static void main(String[] args){
        AddStudent as = new AddStudent();
        as.add();
        System.out.println(as); 
    }
}

带包类的编译和运行

使用javac命令编译时,加上-d
例如:javac -d . HelloWorld.java
上面的.表示当前路径

运行时,使用java 包名.HelloWorld
需要加上包名

使用import关键字导入不同包下的类

将上面的Test01的包名修改一下

package com.monkey1024.oa.system;

    public class Test01{

        public static void main(String[] args){
            AddStudent as = new AddStudent();//报错找不到类
            as.add()
            System.out.println(as); 
        }
    }

上面代码将会报错,因为两个类在不同的包里面,在Test01这个包里面,系统找不到AddStudent类,所以前面需要加上包名:

com.monkey1024.oa.student.AddStudent as = new com.monkey1024.oa.student.AddStudent();

每次用到这个类时都需要写上包名,比较繁琐,我们可以使用import关键字将不同包下的类导入

package com.monkey1024.oa.system;

    import com.monkey1024.oa.student.*//导入这个包下的所有类
    import com.monkey1024.oa.student.AddStudent//导入这个包下的AddStudent类,建议使用这种方式

    public class Test01{

        public static void main(String[] args){
            AddStudent as = new AddStudent();//这样就没问题了
            as.add()
            System.out.println(as); 
        }
    }

注意:java.lang软件包下所有类不需要手动导入,系统自动导入,Object类,String类都在这个包里面

8、访问控制权限

4种访问控制权限

java访问级别修饰符主要包括:private 、protected、public和default(默认),可以限定其他类对该类、属性和方法的使用权限。

注意以上对类的修饰只有:public和default,内部类除外

priavte和public都比较好理解和记忆,这里就不演示了,主要演示一下不同包下的两个具有父子关系的类里面使用protected和default的区别。

创建一个Person类

package com.monkey1024.score.sys;

public class Person{

    String name;

    protected int age;

    void m1(){
        System.out.println("m1");
    }

    protected void m2(){
        System.out.println("m2");
    }
}

创建一个User类,与Person类不在同一个包下

package com.monkey1024.score.buss;

import com.monkey1024.score.sys.Person;
public class User extends Person{

    public void m3(){

        m1();//无法访问,因为父类里面是default修饰的
        m2();
        System.out.println(age);
        System.out.println(name);//无法访问,因为父类里面是default修饰的
    }
}

9、使用Eclipse开发HelloWorld

详情请看:http://www.monkey1024.com/javase/358

10、eclipse的基本配置

详情请看:http://www.monkey1024.com/javase/379

11、eclipse常用快捷键

Elipse常用快捷键

    • 新建 ctrl + n
    • 格式化 ctrl+shift+f
      代码格式错乱时,可以使用这个快捷键修复
    • 导入包 ctrl+shift+o
    • 注释 ctrl+/(添加和删除单行注释),ctrl+shift+/(添加多行注释),ctrl+shift+(删除多行注释)
    • 代码上下移动 选中代码alt+上/下箭头
    • 查看源码 选中类名(F3或者Ctrl+鼠标点击)
    • 查找具体的类 ctrl + shift + t
      在项目里面根据类名查找类
    • 查找具体类的具体方法 ctrl + o
    • 给建议 ctrl+1,根据右边生成左边的数据类型,生成方法
    • 删除代码 ctrl + d
    • 抽取方法alt + shift + m
      将选中的代码抽取成一个方法
    • 改名alt + shift + r
    • 纵向选择alt + shift + a
    • 查找文字ctrl + h
    • 生成构造方法alt + shift + s 菜单显示之后再按c
    • 生成get和set方法alt + shift + s 菜单显示之后再按r
    • 关闭某个类ctrl + w
    • 查看提示alt + /

12、eclipse导入导出项目

详情请看:http://www.monkey1024.com/javase/397

13、Eclipse中debug的使用

详情请看:http://www.monkey1024.com/javase/410

原文地址:https://www.cnblogs.com/zhuifeng-mayi/p/10019319.html