JAVA面向对象

作者: 磊_5d71 | 来源:发表于2018-09-11 06:28 被阅读0次

    一、类与对象的关系

    • 类 抽象的概念

    • 对象 看得见摸得着的具体实体
      类实例化后得到对象

    • 创建宠物猫类

    package com.alan.animal;
    /**
     * 宠物猫类
     * @author alan
     *
     */
    public class Cat {
    
        //属性:昵称、年龄、体重、品种
        
        String name; //昵称 String 默认值null
        int month;   //年龄  int 默认值为 0
        double weight;  //体重 double 默认值为0.0
        String species; //品种
        
        
        //方法:跑动、吃东西
        //跑动的方法
        public void run() {
            System.out.println("小猫快跑");
        }
        //对跑动的方法进行重载
        public void run(String name) {
            System.out.println(name+"快跑");
        }
        //吃东西的方法
        public void eat() {
            System.out.println("小猫吃鱼");     
        }
    
    }
    
    
    • 宠物猫类测试
    package com.alan.animal;
    
    public class CatTest {
    
        public static void main(String[] args) {
    
            //对象实例化
            Cat one = new Cat();
            
            //测试 调用方法
            one.eat();
            one.run();
            one.name = "花花";
            //在类没有初始化的情况下,构造函数对其进行默认的赋值 String 为null ,int 为0,double为0.0
            System.out.println("name="+one.name);
            System.out.println("species="+one.species);
            System.out.println("month="+one.month);
            System.out.println("weight="+one.weight);
            one.run(one.name);
            
        }
    
    }
    

    单一责任原则

    • 一个类只负责一个功能,如果一个类功能太多则耦合性过高,复用的可能性越低。

    实例化对象

    分为两个部分:

    • 声明对象 Cat one 在栈中开辟一块空间,但是没有赋值为null
    • 实例化对象 new cat(); 在堆里面开辟的一块空间
      Cat one = new cat(); // 将堆空间中的地址赋值给栈,栈为指向堆的引用
      image.png

    构造方法(构造函数)

    在new的时候构造方法会被调用,不能被对象单独调用
    1、构造方法与类同名且没有返回值。
    2、只能在对象实例化的时候调用。
    3、当没有指定构造方法时,系统会自动添加无参的构造方法。
    4、就近原则,如果使用name=name赋值,会优先选择同一个作用域,进行赋值,所以要使用不同名字,或者用this.name=name 进行赋值。


    this关键字

    • 代表当前对象的属性或者方法,例如this.name表示当前这个class的属性。


      image.png
      image.png

    this与创建的对象具有相同的ID值,说明this调用的就是当前的对象
    this();可以调用无参构造方法,但是一定是在其他构造方法的第一行。
    其他的方法不能调用构造函数。

    二、Java封装

    将类的某些信息隐藏在类内部,不允许外部程序直接访问。


    image.png
    • 只有get方法 是只读属性
    • 只有set方法 是只写属性

    创建包

    同一个包中不允许出现相同文件名的Cat,引用同名的

    package com.alan.test;
    
    import com.alan.animal.Cat; 
    import com.alan.mechanics.*; //通过.*直接加载到直接的类,不能使用com.alan.*这种方式
    
    public class Test {
    
        public static void main(String[] args) {
            
            com.alan.mechanics.Cat one = new com.alan.mechanics.Cat(); //直接导入的是animal中的Cat,如果需要用mechanics中的需要在代码中加入com等具体的包路径
    //      System.out.println("调用成功!");
        }
    
    }
    

    static关键字

    //static 静态 静态成员 类成员 ,所有对象都使用一块内存空间,所以所有的实例化对象,static修饰的值都相同
    public static int price; //用static修饰后 price变为斜体
    

    1、类对象共享该内存信息
    2、类加载时产生,直到类销毁的时候释放。生命周期长。
    3、静态成员的访问方式,既可以通过对象名访问,也可以通过类名访问。
    one.price (对象名访问) Cat.price(类名访问)
    4、当static修饰方法时,该方法为静态方法,推荐采用类名调用,如Cat.eat();
    5、有静态的属性和方法,静态方法中不能直接访问同一个类的非静态成员,只能直接调用同一个类中的静态成员。只能通过实例化后进行访问没有静态的类,没有静态的局部变量。

    代码块{}

    • 普通代码块:在方法中的代码块叫做普通代码块。顺序执行,先出现,先执行。
    • 构造代码块:在类中的代码块叫构造代码块。创建对象时调用,优先与构造方法执行。
    • 静态代码块:构造代码块加上static关键字,就是静态代码块。类加载时调用,优先于构造代码块执行。 无论实例化多少个对象,只执行一次。

    command+shift+f 行内容过长,换行快捷键。

    三、Java封装综合案例

    • 在方法中通过对象作为参数,传递的是它的引用,可以通过引用获取该对象的所有信息。
    • 数组未实例化会造成空指针异常 java.lang.NullPointerException
    package com.alan.model;
    
    /**
     * 学生类
     * 
     * @author alan
     *
     */
    public class Student {
    
        // 成员属性:学号、姓名、性别、年龄、专业对象
        private String studentNo;
        private String studentName;
        private String studentSex;
        private int studentAge;
        private Subject studentSubject;
    
        // 无参构造
        public Student() {
    
        }
    
        // 带参构造
        public Student(String studentNo, String studentName, String studentSex, int studentAge,Subject studentSubject) {
    
            this.setStudentNo(studentNo);
            this.setStudentName(studentName);
            this.setStudentSex(studentSex);
            this.setStudentAge(studentAge);
            this.setStudentSubject(studentSubject);
        }
    
        /**
         * 获取专业对象,如果没有实例化,先实例化后再返回
         * @return 专业对象
         */
        public Subject getStudentSubject() {
            if(this.studentSubject == null)
                this.studentSubject = new Subject(); //无参构造的重要性就提现出来了
            return studentSubject;
        }
    
        public void setStudentSubject(Subject studentSubject) {
            
            this.studentSubject = studentSubject;
        }
    
        public String getStudentNo() {
            return studentNo;
        }
    
        public void setStudentNo(String studentNo) {
            this.studentNo = studentNo;
        }
    
        public String getStudentName() {
            return studentName;
        }
    
        public void setStudentName(String studentName) {
            this.studentName = studentName;
        }
    
        public String getStudentSex() {
            return studentSex;
        }
    
        public void setStudentSex(String studentSex) {
            this.studentSex = studentSex;
        }
    
        public int getStudentAge() {
            return studentAge;
        }
    
        /**
         * 给年龄赋值。限定大于10,小于50,否则赋值为18
         * 
         * @param studentAge 传入的年龄
         */
        public void setStudentAge(int studentAge) {
            if (studentAge < 10 || studentAge > 50)
                this.setStudentAge(18);
            else
                this.studentAge = studentAge;
        }
    
        /**
         * 学生自我介绍的方法
         * 
         * @return 自我介绍信息,包括姓名、学号、性别、年龄
         *
         */
        public String introduction() {
    
            String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n学号:" + this.getStudentNo() + "\n性别:"
                    + this.getStudentSex() + "\n年龄:" + this.getStudentAge()+ "\n所报专业名称:" + this.getStudentSubject().getSubjectName() + "\n学制年限:"
                            + this.getStudentSubject().getSubjectLife();
            return str;
    
        }
    
        /**
         * 学生自我介绍的方法
         * 
         * @param subjectName 所学专业名称
         * @param subjectLife 学制年限
         * @return 自我介绍信息,包括姓名、学号、性别、年龄、所学专业名称、学制年限
         */
    
        public String introduction(String subjectName, int subjectLife) {
            String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n学号:" + this.getStudentNo() + "\n性别:"
                    + this.getStudentSex() + "\n年龄:" + this.getStudentAge() + "\n所报专业名称:" + subjectName + "\n学制年限:"
                    + subjectLife;
            return str;
        }
        
        /**
         * 学生自我介绍的方法
         * @param sub1 传入学科对象
         * @return 自我介绍信息,包括姓名、学号、性别、年龄、所学专业名称、学制年限
         */
        public String introduction(Subject sub1) {
    
            String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n学号:" + this.getStudentNo() + "\n性别:"
                    + this.getStudentSex() + "\n年龄:" + this.getStudentAge() + "\n所报专业名称:" + sub1.getSubjectName()
                    + "\n学制年限:"+sub1.getSubjectLife();
            return str;
    
        }
    }
    

    学科类

    package com.alan.model;
    
    public class Subject {
    
        // 成员属性:学科名称、学科编号、学制年限、选课学生数组、报名选修的学生个数
        private String subjectName;
        private String subjectNo;
        private int subjectLife;
        private Student[] myStudents;
        private int studentNum;
        
    
        // 无参构造方法,定义一下,防止出现意外的错误。
        public Subject() {
    
        }
    
        // 带参构造方法 对学科名称、学科编号、学制年限赋值
        public Subject(String subjectName, String subjectNo, int subjectLife) {
    
            this.setSubjectName(subjectName); // 尽量采用此种赋值方式,而不用this.subjectName=subjectName
            this.setSubjectNo(subjectNo); // 因为set方法中有时候会包含一些逻辑校验
            this.setSubjectLife(subjectLife);
        }
        
        // 带参构造方法 对全部属性赋值
        public Subject(String subjectName, String subjectNo, int subjectLife,Student[] myStudent) {
    
            this.setSubjectName(subjectName); // 尽量采用此种赋值方式,而不用this.subjectName=subjectName
            this.setSubjectNo(subjectNo); // 因为set方法中有时候会包含一些逻辑校验
            this.setSubjectLife(subjectLife);
            this.setMyStudents(myStudent);
        }
    
        /**
         * 获取选修专业学生信息,如果保存学生信息的数组没有被初始化,则初始化长度为200
         * @return 保存学生信息的数组
         */
        public Student[] getMyStudents() {
            if(myStudents == null)
                myStudents = new Student[200];
            return myStudents;
        }
    
        public void setMyStudents(Student[] myStudents) {
            this.myStudents = myStudents;
        }
    
        public String getSubjectName() {
            return subjectName;
        }
    
        public void setSubjectName(String subjectName) {
            this.subjectName = subjectName;
        }
    
        public String getSubjectNo() {
            return subjectNo;
        }
    
        public void setSubjectNo(String subjectNo) {
            this.subjectNo = subjectNo;
        }
    
        public int getSubjectLife() {
            return subjectLife;
        }
    
        // 设置学制年限,必须大于0
        public void setSubjectLife(int subjectLife) {
            if (subjectLife <= 0)
                return;
            this.subjectLife = subjectLife;
        }
        public int getStudentNum() {
            return studentNum;
        }
    
        public void setStudentNum(int studentNum) {
            this.studentNum = studentNum;
        }
    
        /**
         * 专业介绍的方法
         * @return 专业介绍的相关信息 专业名称、专业编号、学制年限
         */
        public String info() {
            String str = "专业信息如下: \n" + "专业名称:" + this.getSubjectName() + "\n专业编号:" + this.getSubjectNo() + "\n学制年限:"
                    + this.getSubjectLife();
            return str; 
        }
        
        public void addStudent(Student stu) {
            
            //1、将学生保存到数组中
            for(int i=0;i<this.getMyStudents().length;i++) {
                
                if(this.getMyStudents()[i]==null) {
                    this.getMyStudents()[i] = stu;
                    //2、将学生的个数保存到studentNum中
                    this.studentNum = i+1;
                    return;
                }
            }
        }
    
    }
    

    测试

    package com.alan.test;
    
    import com.alan.model.Student;
    import com.alan.model.Subject;
    
    public class NewTest {
    
        public static void main(String[] args) {
    
            
            Subject sub1 = new Subject("计算机科学与应用","J0001",4);
            System.out.println(sub1.info());
            System.out.println("=======================");
            Student stu1 = new Student("S01","张三","男",58,sub1);
            System.out.println(stu1.introduction());
            System.out.println("=======================");
            Student stu2 = new Student("S02","李四","男",19,sub1);
            System.out.println(stu2.introduction("计算机科学与技术", 4));
            System.out.println("=======================");
            Student stu3 = new Student("S03","王五","女",17,sub1);
            System.out.println(stu3.introduction(sub1));
            
            System.out.println("=======================");
            sub1.addStudent(stu1);
            sub1.addStudent(stu2);
            sub1.addStudent(stu3);
            
            System.out.println("学生的总数为:"+sub1.getStudentNum());
        }
    
    }
    

    四、Java继承

    • 子类继承父类可以直接获取父类的非私有成员(包括属性和方法)
      command+shift+o 快速导入包
    • 父类不能访问子类的特有成员(包括属性和方法)

    方法重写

    1、子类重写父类的方法
    2、方法名相同、参数列表相同(参数顺序、个数、类型),方法返回值相同
    3、方法的访问修饰符是允许有变化的,但是访问范围要大于等于父类的访问范围)
    4、与方法参数名无关(string name)可以换成(string month)


    访问修饰符

    • private 修饰的成员(属性和方法)只能在本类中访问
    • public 修饰的成员(属性和方法)访问范围最大,可以在本类、子类、跨包中访问。
    • protected 修饰的成员(属性和方法) 可以在本类,同包子类,同包非子类,跨包子类使用。跨包非子类中不允许
    • 默认 修饰的成员(属性和方法) 可以在本类,同包子类,同包非子类,不允许在跨包子类/非子类访问


      image.png

    super关键字

    • 父类对象的引用,当子类中重写父类方法,需要调用父类的原有方法,需要加上super关键字。
    • 子类构造默认调用父类的无参构造,可以通过super关键字调用父类允许被访问的其他构造方法。必须放在第一行

    继承的初始化顺序

    • 首先加载父类的静态成员,父类的静态代码块

    • 接着加载子类的静态成员,子类的静态代码块

    • 父类的成员属性赋值

    • 父类的构造代码块

    • 父类的构造函数

    • 子类的构造代码块

    • 子类的构造函数


      image.png
    • this 与 super 分别指定当前类和父类 不能在静态方法中使用


      image.png

    Object类

    • Object类是所有类的父类
      Object中的 equals 相当于== 比较两个字符串的引用是否相同
      String中的 equals 被重写 比较两个字符串的值是否相等

    • tostring也是重写率非常高的一个方法

        /*tostring测试
         * 1、输出对象名时,默认会调用toString方法
         * 2、输出对象为:类型信息+@+地址信息 如:com.alan.animal.Animal@7b1d7fff
         * */
      

    final关键字

    • final修饰class表示该类没有子类,不允许被继承。
    • String类 System类都是final修饰的
    • final 修饰方法,该方法不允许被重写,但是可以正常被子类继承使用
    • final 修饰方法内局部变量,该局部变量不允许被再次修改。
    • final 修饰类成员属性,没有赋值的话,只能在构造函数或者构造代码块中进行赋值
    • final修饰引用类型的变量,初始化后不能再指向另一个对象,但对象的内容可以改变。

    注释简介

    快捷键 option+/
    @Override 给编译器看的,编译之后注解就没有用了。

    五、Java设计模式

    image.png

    单例模式

    • 目的:使类的一个对象成为该类系统的唯一的实例。

    • 定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供


      image.png
    • 饿汉式代码实现
      线程安全的

    package com.alan.singleton;
    
    //饿汉式:创建对象实例的时候直接初始化
    public class SingletonOne {
    
        //1、创建类中的私有构造
        private SingletonOne() {
            
        }
        
        //2、创建该类型的私有静态实例
        private static SingletonOne instance = new SingletonOne();
        
        
        //3、创建公有静态方法返回静态实例对象
        public static SingletonOne getInstance() {
            return instance;
        }
        
        
    }
    

    测试

    
    import com.alan.singleton.SingletonOne;
    
    public class Test {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            
            //  SingletonOne one = new SingletonOne();  构造方法已经私有化,这种方式不允许
            
            SingletonOne one = SingletonOne.getInstance();
            SingletonOne two = SingletonOne.getInstance();
    
            System.out.println(one);
            System.out.println(two);  //所有的引用都会指向同一个对象
    
            
        }
    
    }
    
    
    • 懒汉式代码实现
      线程不安全的
      通过1、同步锁 2、双重校验锁 3、静态内部类 4、枚举 解决线程不安全问题。
    package com.alan.singleton;
    
    // 懒汉式单例模式
    public class SingletonTwo {
    
        // 1、创建私有构造方法
        private SingletonTwo() {
            
        }
        
        // 2、创建私有静态实例,静态的实例被共享,一定需要此修饰符
        private static SingletonTwo instance = null;
        
        // 3、创建静态公有方法返回静态实例对象
        public static SingletonTwo getInstance() {
            if(instance == null)
                instance = new SingletonTwo();
            return  instance;
        }
    }
    
    image.png

    六、多态

    封装和继承都是为多态准备的,是最核心也是最重要的概念。
    多态的必要条件:
    1、继承
    2、重写
    3、父类引用指向子类对象

    向上转型

    package com.alan.test;
    
    import com.alan.animal.Animal;
    import com.alan.animal.Cat;
    import com.alan.animal.Dog;
    
    public class Test {
    
        public static void main(String[] args) {
            
            Animal one = new Animal();
            /* 向上转型、隐式转型、自动转型
            父类引用指向子类的对象,可以调用子类重写父类的方法,以及父类派生的方法,无法调用子类独有方法
            小类转型为大类
            */
            Animal two = new Cat();
            Animal three = new Dog();
            one.eat();
            two.eat();
            three.eat();
        }
    }
    

    向下转型

    package com.alan.test;
    
    import com.alan.animal.Animal;
    import com.alan.animal.Cat;
    import com.alan.animal.Dog;
    
    public class Test {
    
        public static void main(String[] args) {
            
            Animal one = new Animal();
            /* 向上转型、隐式转型、自动转型
            父类引用指向子类的对象,可以调用子类重写父类的方法,以及父类派生的方法,无法调用子类独有方法
            小类转型为大类
            */
            Animal two = new Cat();
            Animal three = new Dog();
            
            one.eat();
            two.eat();
            three.eat();
            System.out.println("============================");
            
            /*向下转型、强制类型转换
             * 子类引用指向父类对象、此处必须进行强转,可以调用子类特有的方法
             * 必须满足转换条件才能转换
             * */
            Cat temp = (Cat)two;
            temp.eat();
            temp.run();
            
        }
    
    }
    

    抽象类

    • 抽象类不允许实例化,可以通过向上转型,指向子类实例


      image.png

    抽象方法

    不允许包含方法体;子类中需要重写父类的抽象方法。否则,子类也要设置为抽象类。

    • 包含抽象方法的类一定是抽象类。
    • 抽象类可以没有抽象方法。

    接口

    package com.alan.tel;
    /**
     * 具有上网功能的接口
     * @author alan
     *
     */
    
    //接口可以设置的访问修饰符:public 默认
    
    public interface INet {
        //接口中抽象方法可以不写abstract关键字,访问修饰符默认为public
        public void network();
        
        //default:默认方法 可以带方法体  jdk1.8之后新增的方法
        //可以在实现类中重写,并可以通过接口的引用调用
        default public void connection() {
            System.out.println("我是接口中的默认方法");
        }
        
        //static:静态方法 可以带方法体  jdk1.8之后新增的方法
        //不可以在实现类中重写,可以通过接口调用
        static void stop() {
            System.out.println("我是接口中的静态方法");
        }
        
        //接口中可以包含常量,默认会自动加上public final static
        
        int TEMP =20;
    }
    

    • 一个类可以实现多个接口
    • 继承的父类和实现的接口中有同名方法,默认是实现父类中的同名方法。
    • 继承的父类的属性和接口中的常量同名,编译器无法分辨,会报错。

    接口也可以实现继承关系,并且可以继承多个父接口

    内部类

    在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
    与之对应,包含内部类的类叫外部类。
    分为:1、成员内部类 2、静态内部类 3、方法内部类 4、匿名内部类

    • 成员内部类
    package com.alan.people;
    
    
    //外部类
    public class Person {
        
        int age;
        
        public Heart getHeart() {
            
            return new Heart();
        }
        
        //成员内部类
        /*
         * 1、内部类在外部使用时,无法直接实例化,需要借由外部类信息才能完成实例化
         * 2、内部类的访问修饰符,可以任意,但是访问范围会受到影响
         * 3、内部类可以直接访问外部类的成员;如果出现同名属性,优先访问内部类中的定义
         * 4、可以使用外部类.this.成员方式,访问外部类中的同名信息
         * 5、外部类访问内部类信息,需要通过内部类实例,无法直接访问。
         * 6、内部类编译后.class文件命名:外部类$内部类.class
         * */
        class Heart{
            
            public String beat() {
                return "心脏在跳动";
            }
        }
    
    }
    
    package com.alan.people;
    
    public class PersonTest {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            
            Person lili = new Person();
            
            //获取内部类对象实例,方式1:new 外部类. new 内部类
            Person.Heart myheart = new Person().new Heart();
            System.out.println(myheart.beat());
    
            //获取内部类对象实例,方式2:外部类对象.new 内部类
            myheart = lili.new  Heart();
            System.out.println(myheart.beat());
    
            //获取内部类对象实例,方式3:外部类调用方法返回内部类
            myheart = lili.getHeart();
            System.out.println(myheart.beat());
    
        }
    
    }
    
    • 静态内部类
      有static修饰,静态内部类对象可以不依赖于外部对象,直接创建。
    • 方法内部类


      image.png

    相关文章

      网友评论

        本文标题:JAVA面向对象

        本文链接:https://www.haomeiwen.com/subject/xuhhgftx.html