zoukankan      html  css  js  c++  java
  • 面向对象之封装

       类和对象的关系

    • 类就是:对现实生活中事物的描述。
    • 对象:就是这类事物,实实在在存在的个体。

       比如:

       现实生活中的对象:张三,李四。

       想要描述:提取对象中共性内容。对具体对象的抽象。

       描述时,这些对象的共性有:姓名,年龄,性别,学习java的功能。映射到java中,描述就是class定义的类具体对象就是对应java在堆内存中用new建立的实体

       (其实定义类)描述事物,其实就是在描述事物的属性和行为。属性对应类中变量,行为对应类中的函数(方法)。属性和行为共同成为类中的成员(成员变量和成员函数)。

       成员变量和局部变量的区别:

      1、作用域

        成员变量作用于整个类中。

        局部变量作用于函数中,或者语句中。

      2、在内存中的位置

        成员变量:在堆内存中,因为对象的存在,才在内存中存在。

        局部变量:存在栈内存中。

       匿名对象

       匿名对象使用方式:

    1. 当对对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。如果对一个对象进行多个成员调用,必须给这个对象起个名字。
    2. 可以将匿名对象作为实际参数进行传递。

       示例代码如下:

    class Car {
        //描述颜色
        String color = "红色";
        //描述轮胎数
        int num = 4;
        
        //运行行为
        void run() {
            System.out.println(color+".."+num);
        }
        
        public static void main(String[] args) {
            Car c = new Car();
            c.num = 10;
            //run();
        }
    }
    class CarDemo {
        public static void main(String[] args) {
            //生产汽车,在java中通过new操作符来完成
            //其实就是在堆内存产生一个实体
            Car c = new Car();//c就是一个类类型变量。记住:类类型变量指向对象。
            
            c.color = "蓝色";
            c.run();
    //      Car c1 = new Car();
    //      c1.run();
            
    //      Car c = new Car();
    //      c.num = 5;
    //      new Car().num = 5;
    //      new Car().color = "blue";
    //      new Car().run();
            //匿名对象使用方式一:当对对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化
            //如果对一个对象进行多个成员调用,必须给这个对象起个名字
            
            //匿名对象使用方式二:可以将匿名对象作为实际参数进行传递
    //      Car q = new Car();
    //      show(q);
            show(new Car());
        }
        public static void show(Car c) {
            c.num = 3;
            c.color = "black";
            c.run();
        }
    }

       封装

       private:私有,权限修饰符,用于修饰类中的成员(成员变量、成员函数)。私有只在本类中有效。

       注意:私有仅仅是封装的一种表现形式

       构造代码块

       构造代码块——定义的是不同对象共性的初始化内容。

       作用:给对象进行初始化,对象一建立就运行,而且优先于构造函数执行。

       和构造函数的区别:构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化。

       对象一建立就会调用与之对应的构造函数。构造函数和一般函数在写法上有不同,在运行上也有不同。构造函数是在对象一建立就运行,给对象初始化。而一般函数是对象调用才执行,是给对象添加对象具备的功能。一个对象建立,构造函数只运行一次,而一般函数可以被该对象调用多次。

       示例代码:

    class Person {
        private String name;
        private int age;
        
        /*
         * 构造代码块——定义的是不同对象共性的初始化内容
         * 作用:给对象进行初始化,对象一建立就运行,而且优先于构造函数执行
         * 和构造函数的区别:构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化
         */
        {
            //System.out.println("person code run");
            cry();
        }
        
        Person() {
            System.out.println("A: name="+name+", age="+age);
            //cry();
        }
        
        Person(String n) {
            name = n;
            System.out.println("B: name="+name+", age="+age);
            //cry();
        }
        
        public void setName(String n) {
            name = n;
        }
        
        public String getName() {
            return name;
        }
        
        Person(String n, int a) {
            name = n;
            age = a;
            System.out.println("C: name="+name+", age="+age);
            //cry();
        }
        
        public void cry() {
            System.out.println("cry....");
        }
        
        public void setAge(int a) {
            if(a > 0 && a < 130) {
                age = a;
                speak();
            }
            else
                System.out.println("非法年龄");
        }
        
        public int getAge() {
            return age;
        }
        
        void speak() {
            System.out.println("age="+age);
        }
        
    }
    public class PersonDemo {
        public static void main(String[] args) {
            Person p1 = new Person();//对象一建立就会调用与之对应的构造函数
    //      p1.cry();//构造函数和一般函数在写法上有不同,在运行上也有不同。构造函数是在对象一建立就运行,给对象初始化
                     //而一般函数是对象调用才执行,是给对象添加对象具备的功能
                     //一个对象建立,构造函数只运行一次,而一般函数可以被该对象调用多次
            Person p2 = new Person("lisi");
    //      p2.setName("libusi");
    //      p2.setName("LIBUHSI");
    //      System.out.println(p2.getName());
    //      Person p2 = new Person("LISI");
    //      Person p3 = new Person("WNAGWU", 10);
        }
    }

       this

       this:看上去,是用于区分局部变量和成员变量同名情况。

       this:代表本类的对象,this代表它所在函数所属对象的引用,简单说:哪个对象在调用this所在的函数,this就代表哪个对象。

       示例代码:

    class Person {
        private String name;
        private int age;
        /*
         * this:看上去,是用于区分局部变量和成员变量同名情况
         * this:代表本类的对象,this代表它所在函数所属对象的引用,简单说:哪个对象在调用this所在的函数,this就代表哪个对象
         */
        Person() {
            
        }
        
        Person(String name) {
            this();
            this.name = name;
        }
        
        Person(int age) {
            this.age = age;
        }
        
        public Person(String name, int age) {
    //      this.name = name;
            this(name);  //p(name);用于构造语句之间互相调用
            this.age = age;
        }
        
        public void setName(String n) {
            name = n;
        }
        
        public String getName() {
            return name;
        }
        
        public void setAge(int a) {
            age = a;
        }
        
        public int getAge() {
            return age;
        }
        
        public boolean compare(Person per) {
            if(this == per) {
                return true;
            }
            if(this.getName().equals(per.getName()) &&
                    this.getAge() == per.getAge()) {
                return true;
            } else {
                return false;
            }
        }
        
        public void speak() {
            System.out.println("name="+name+"...age="+age);
            show();
        }
        
        public void show() {
            System.out.println(this.name);
        }
        
        /*
         * this的应用
         * 比较年龄是否相同的?
         */
        public boolean compareAge(Person per) {
            return this.age == per.age;
        }
    }
    public class ThisDemo {
    
        public static void main(String[] args) {
    //      Person p1 = new Person(20);
    //      Person p2 = new Person(25);
    //      boolean b = p1.compareAge(p2);
    //      System.out.println(b);
            Person p = new Person("lisi", 30);
    //      Person p1 = new Person("zhangsan");
    //      p.speak();
    //      p1.speak();
        }
    
    }

       static

       static:用法:是一个修饰符,用于修饰成员(成员变量、成员函数)。

       特点:

    1. 随着类的加载而加载,类的消失而消失,说明它的生命周期最长。
    2. 优先于对象存在,明确一点:静态是先存在的,对象是后存在的。
    3. 被所有对象所共享。
    4. 可以直接被类名所调用。

       静态的成员变量,也叫类变量。成员变量,也叫实例变量

       实例变量和类变量的区别:

      1、存在位置

        类变量随着类的加载而存在于方法区中。

        实例变量随着对象的建立而存在于堆内存中。

      2、生命周期

        类变量生命周期最长,随着类的消失而消失。

        实例变量生命周期随着对象的消失而消失。

       静态使用注意事项:

    1. 静态方法只能访问静态成员,非静态方法既可以静态也可以访问非静态。
    2. 静态方法中不可以定义this、super关键字。因为静态优先于对象存在,所以静态方法中不可以出现this。
    3. 主函数是静态的。

       静态有利有弊:

    • 利处:对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象中都存储一份;可以直接类名调用。
    • 弊端:生命周期过长,访问出现局限性(静态虽好,只能访问静态)。

       示例代码:

    class S_Person {
        String name;//成员变量,也叫实例变量
        int age;
        /*
         * static:用法:是一个修饰符,用于修饰成员(成员变量、成员函数)
         * 特点:
         * 1、随着类的加载而加载,类的消失而消失,说明它的生命周期最长
         * 2、优先于对象存在,明确一点:静态是先存在的,对象是后存在的
         * 3、被所有对象所共享
         * 4、可以直接被类名所调用
         */
        static String country = "CN";//静态的成员变量,也叫类变量
        /*
         * 实例变量和类变量的区别:
         * 1、存在位置——类变量随着类的加载而存在于方法区中
         *          实例变量随着对象的建立而存在于堆内存中
         * 2、生命周期——类变量生命周期最长,随着类的消失而消失
         *          实例变量生命周期随着对象的消失而消失
         */
        /*
         * 静态使用注意事项:
         * 1、静态方法只能访问静态成员
         *   非静态方法既可以静态也可以访问非静态
         * 2、静态方法中不可以定义this、super关键字
         *   因为静态优先于对象存在,所以静态方法中不可以出现this
         * 3、主函数是静态的
         * 
         * 静态有利有弊
         * 利处:对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象中都存储一份
         *    可以直接类名调用
         * 弊端:生命周期过长,访问出现局限性(静态虽好,只能访问静态)
         */
        /*static public void setCountry(String country) {
            //this.country = country; //Cannot use this in a static context
            S_Person.country = country;
        }*/
        
        public S_Person() {
            
        }
        
        public S_Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        public static void show() {
            System.out.println("::"+country);
        }
        public void info() {
            System.out.println("姓名:"+this.name+",年龄:"+this.age+",城市:"+this.country);
        }
    }
    public class StaticDemo {
    
        public static void main(String[] args) {
            S_Person.show();
    //      System.out.println(S_Person.country);
    //      S_Person p = new S_Person();
    //      System.out.println(p.country);
            
        }
    
    }

       主函数(main)

       主函数:是一个特殊的函数,作为程序的入口,可以被JVM调用。

       主函数的定义:

    1. public:代表着该函数访问权限是最大的。
    2. static:代表主函数随着类的加载就已经存在了。
    3. void:主函数没有具体的返回值。
    4. main:不是关键字,但是是一个特殊的单词,可以被JVM设别。
    5. String[] args:函数的参数,参数类型是一个数组,该数组中的元素是字符串,字符串类型的数组。

       主函数是固定格式的:JVM设别。JVM在调用主函数时,传入的是new String[0];

       示例代码:

    public class StaticDemo1 {
        
        public static void main(String[] args) {
            System.out.println(args[0]);//会出现ArrayIndexOutOfBoundsException异常
        }
        
    }

       静态代码块

       格式:

    static {
        静态代码块中的执行语句
    }

       特点:随着类的加载而执行,只执行一次,并优先于主函数。用于给类进行初始化

       示例代码如下:

    class StaticCode {
        int num = 9;
        StaticCode() {
            System.out.println("b");
        }
        static {
            System.out.println("a");
        }
        /*
         * 构造代码块
         */
        {
            System.out.println("c"+this.num);
        }
        StaticCode(int x) {
            System.out.println("d");
        }
        public static void show() {
            System.out.println("show run");
        }
    }
    public class StaticCodeDemo {
        
        static {
            //System.out.println("b");
        }
        public static void main(String[] args) {
            //new StaticCode();//将StaticCode.class这个类加载进内存
            //new StaticCode();//StaticCode.class已经加载进内存,不会再次打印输出语句!!!
            //System.out.println("Hello World!!!");
            //StaticCode.show();
            //StaticCode s = null;//特别注意:StaticCode类没有被加载进内存
            //s = new StaticCode();
            
            new StaticCode(4); //打印 a c d
        }
        static {
    //      System.out.println("c");
        }
    
    }

       练习:通过以下程序代码,试说明Nei_Person p = new Nei_Person("张三", 23);该句话都做了什么事情?

       代码:

    class Nei_Person {
        private String name = "haha";
        private int age;
        private static String country = "CN";
        
        Nei_Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        {
            System.out.println(name+".."+age);
        }
        
        public void setName(String name) {
            this.name = name;
        }
        
        public void speak() {
            System.out.println(this.name+"..."+this.age);
        }
        
        public static void showCountry() {
            System.out.println("country="+country);
            method();
        } 
        
        public static void method() {
            System.out.println("method run");
        }
    }
    public class Nei_PersonDemo {
        
        public static void main(String[] args) {
            Nei_Person p = new Nei_Person("张三", 23);
            p.setName("lisi");
        }
    
    }

       解:

       Nei_Person p = new Nei_Person("张三", 23);该句话都做了什么事情?

    1. 因为new用到了Nei_Person.class,所以会找到Nei_Person.class文件并加载到内存。
    2. 执行该类中的static代码块,如果有的话,给Nei_Person.class类进行初始化。
    3. 在堆内存中开辟空间,分配内存地址。
    4. 在堆内存中建立对象的特有属性,并进行默认初始化。
    5. 对属性进行显示初始化
    6. 对对象进行构造代码块初始化。
    7. 对对象进行对应的构造函数初始化。
    8. 将内存地址赋给栈内存中的p变量。

       

       什么时候使用静态?

       要从两方面下手:因为静态修饰的内容有成员变量和函数。

      1、什么时候定义静态变量(类变量)呢?

        当对象中出现共享数据时,该数据被静态所修饰。对象中的特有数据要定义成非静态,存在于堆内存中。

      2、什么时候定义静态函数呢?

        当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的

       静态的应用——以下例说明

       每个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装,以便复用。

       示例:

    /**
     * 这是一个可以对数组进行操作的工具类,该类中提供了获取最值、排序等功能
     * @author 李阿昀
     * @version V1.1
     */
    class ArrayTool {
        /**
         * 空参数构造函数
         * 一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致。
         * 如果类被public修饰,那么默认的构造函数也带public修饰
         * 如果类没有被public修饰,那么默认的构造函数也没有public修饰
         * 
         * 简而言之:默认构造函数的权限是随着类的变化而变化的
         */
        private ArrayTool() {}
        /**
         * 获取一个整型数组中的最大值
         * @param arr 接受一个int类型的数组
         * @return 会返回一个该数组中的最大值
         */
        public static int getMax(int[] arr) {
            int max = 0;
            for(int x = 1; x < arr.length; x++)
                if(arr[x] > arr[max])
                    max = x;
            return arr[max];
        }
        /**
         * 获取一个整型数组中的最小值
         * @param arr 接受一个int类型的数组
         * @return 会返回一个该数组中的最小值
         */
        public static int getMin(int[] arr) {
            int min = 0;
            for(int x = 1; x < arr.length; x++)
                if(arr[x] < arr[min])
                    min = x;
            return arr[min];
        }
        /**
         * 给int数组进行选择排序
         * @param arr 接受一个int类型的数组
         */
        public static void selectSort(int[] arr) {
            for(int x = 0; x < arr.length - 1; x++) {
                for(int y = x + 1; y < arr.length; y++) {
                    if(arr[x] > arr[y]) {
                        swap(arr, x, y);
                    }
                }
            }
        }
        /**
         * 给int数组进行冒泡排序
         * @param arr 接受一个int类型的数组
         */
        public static void bubbleSort(int[] arr) {
            for(int x = 0; x < arr.length - 1; x++) {
                for(int y = 0; y < arr.length - x - 1; y++) {
                    if(arr[y] > arr[y+1]) {
                        swap(arr, y, y+1);
                    }
                }
            }
        }
        /**
         * 给数组中的元素进行位置的置换
         * @param arr 接受一个int类型的数组
         * @param a 要置换的位置
         * @param b 要置换的位置
         */
        private static void swap(int[] arr, int a, int b) {
            int temp = arr[a];//置换私有化
            arr[a] = arr[b];
            arr[b] = temp;
        }
        /**
         * 用于打印数组中的元素。打印形式是:[element1, element2, ...]
         * @param arr 接受一个int类型的数组
         */
        public static void printArray(int[] arr) {
            System.out.print("[");
            for(int x = 0; x < arr.length; x++) {
                if(x != arr.length - 1)
                    System.out.print(arr[x] + ", ");
                else
                    System.out.println(arr[x] + "]");
            }
        }
    }

       虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作,但是依然发现了问题:

    1. 对象是用于封装数据的,可是ArrayTool对象并未封装特有数据。
    2. 操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。

       这时就考虑,让程序更严谨,是不需要对象的。可以将ArrayTool中的方法都定义成static的。直接通过类名调用即可。

       将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的。为了更为严谨,强制让该类不能建立对象。可以通过将构造函数私有化完成。。

       接下来,将ArrayTool.class文件发送给其他人,其他人只要将该文件设置到classpath路径下,就可以使用该工具类。但是,很遗憾,该类中定义了多少个方法,对方不清楚,因为该类并没有使用说明书。开始制作程序的说明书,java的说明书通过文档注释来完成。

       

       单例设计模式

       设计模式:解决某一类问题最行之有效的方法。JAVA中有23种设计模式。

       单例设计模式:解决一个类在内存只存在一个对象

       想要保证对象唯一:

    1. 为了避免其他程序过多建立该类对象,先禁止其他程序建立该类对象。
    2. 还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
    3. 为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。

       这3步怎么用代码体现?

    1. 将构造函数私有化。
    2. 在类中创建一个本类对象。
    3. 提供一个方法可以获取到该对象。

       对于事物该怎么描述,还怎么描述。当需要将该事物的对象保证在内存中唯一时,就将以上的3步加上即可。

      示例代码:

    class Single {
        
        private int num;
        
        public void setNum(int num) {
            this.num = num;
        }
        
        public int getNum() {
            return num;
        }
        private Single() {
            
        }
        private static Single s = new Single();
        
        public static Single getInstance() {
            return s;
        }
    }
    
    class Student {
        private int age;
        
        private static Student s = new Student();
        
        private Student() {
            
        }
        
        public static Student getStudent() {
            return s;
        }
        
        public void setAge(int age) {
            this.age = age;
        } 
        
        public int getAge() {
            return age;
        }
        
        
    }
    
    public class SingleDemo {
    
        public static void main(String[] args) {
            Single ss = Single.getInstance();
            Single s1 = Single.getInstance();
            ss.setNum(20);
            System.out.println(s1.getNum());
            
            Student stu1 = Student.getStudent();
            stu1.setAge(30);
            
            Student stu2 = Student.getStudent();
            stu1.setAge(3);
            
            
        }
    
    }

       单例设计模式可分为饿汉式与懒汉式(面试常考)(以下例代码说明):

    class Single1 {
        
        /*  
         * 这个是先初始化对象。称为:饿汉式
         * Single1类一进内存,就已经创建好了对象
         * 开发原则:定义单例,建议使用饿汉式
            private static Single1 s1 = new Single1();
            
            private Single1() {
                
            }
            
            public static Single1 getInstance() {
                return s1;
            }
        */
        
        /*
         * 对象是方法被调用时,才初始化,也叫做对象的延时加载。称为:懒汉式
         * Single1类进内存,对象还没存在,只有调用getInstance()方法时,才建立对象
         */
        private static Single1 s1 = null;
        
        private Single1() {
            
        }
        
        //synchronized相当于上了一个锁,但程序效率降低了
        public static Single1 getInstance() {
            if(s1 == null) { //双重判断,可解决这个问题(涉及多线程)
                //--->B(挂着了)
                synchronized (Single1.class) {
                    if(s1 == null) {
                        //--->A(挂着了)
                        s1 = new Single1();
                    }
                }
            }
            return s1;
        }
    }
  • 相关阅读:
    C#深入浅出 修饰符(二)
    HDU 5785 Interesting
    HDU 5783 Divide the Sequence
    HDU 5781 ATM Mechine
    UVA 714 Copying Books
    uva 1471 Defense Lines
    UVA 11134 Fabled Rooks
    UVA 11572 Unique Snowflakes
    UVA 11093 Just Finish it up
    UVA 10954 Add All
  • 原文地址:https://www.cnblogs.com/yerenyuan/p/5236511.html
Copyright © 2011-2022 走看看