• 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

  • 相关阅读:
    集合框架之——迭代器并发修改异常ConcurrentModificationException
    Python day 3 (3) 判断与循环
    hdu 5335 Walk Out(bfs+斜行递推) 2015 Multi-University Training Contest 4
    hdu 2473 Junk-Mail Filter(并查集_虚节点)2008 Asia Regional Hangzhou
    hdu 1573 x问题(中国剩余定理)HDU 2007-1 Programming Contest
    hdu 3461 Code Lock(并查集)2010 ACM-ICPC Multi-University Training Contest(3)
    hdu 2155 小黑的镇魂曲(dp) 2008信息工程学院集训队——选拔赛
    hdu 4081 Qin Shi Huang's National Road System(最小生成树+dp)2011 Asia Beijing Regional Contest
    hdu 3938 Portal(并查集+离线+kruskal)2011 Multi-University Training Contest 10
    hdu 3172 Virtual Friends(并查集)University of Waterloo Local Contest 2008.09
  • 原文地址:https://www.cnblogs.com/zhuifeng-mayi/p/10019319.html
Copyright © 2020-2023  润新知