面向对象三大特征——封装

目录

1. 封装概述(封装与隐藏)

2. private关键字

3. Getter & Setter方法

4. 变量访问原则和this关键字

5. 构造方法

5.1 构造方法概述

5.2 构造方法和set方法的比较

6. 静态

6.1 静态概述

6.2 静态效果

6.3 静态变量和非静态变量的区别

6.4 工具类

7. 代码块

7.1 概述

7.2 局部代码块

7.3 构造代码块

7.4 静态代码块


1. 封装概述(封装与隐藏)

封装:隐藏事物的属性和实现细节,对外提供公共的访问方式。

为什么需要封装?封装的作用和含义?

  • 我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内部的结构吗?有必要碰电动机吗?

  • 我要开车,...

我们程序设计追求“高内聚,低耦合”。

  • 高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;

  • 低耦合 :仅对外暴露少量的方法用于使用。

封装性的设计思想

隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露 的暴露出来。这就是封装性的设计思想。

封装的好处:

  • 隐藏了事物的实现细节

  • 提高了代码的复用性

  • 提高了安全性

封装的原则:

  • 隐藏事物的属性

  • 隐藏事物的实现细节

  • 对外提供公共的访问方式

  • 代码体会:

public class Animal {
    String name;
    int legs;
​
    void move() {
        System.out.println("我叫" + name + ",我用" + legs + "条腿跑步!");
    }
}
​
public class AnimalTest {
    public static void main(String[] args) {
        Animal pig = new Animal();
        pig.name = "佩奇";
        pig.legs = 4; // 修改一下试试?
        pig.move();
    }
}
  • 使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。

2. private关键字

  • 含义:私有的

  • 可以修饰的内容:

    • 修饰成员变量

    • 修饰成员方法

    • 修饰构造方法

    • 修饰内部类

  • 修饰之后的效果:被private修饰的成员,只能在本类中被访问。

  • 注意事项:private只是封装的一种体现形式,封装还可以使用其他的修饰符来完成。

  • 示例:

    public class Animal {
        private String name;
        private int legs;
    ​
        void move() {
            System.out.println("我叫" + name + ",我用" + legs + "条腿跑步!");
        }
    }

3. Getter & Setter方法

  • 当属性被私有之后,外界无法直接访问,所以需要提供公共的访问方式,让外界可以间接的访问属性。对于当前类,就可以控制外界访问属性的方式。(我让你怎么访问,你就只能怎么访问)

  • 一般提供get方法,获取成员变量的值;提供set方法,设置成员变量的值。

  • 示例:

    public class Animal {
        private String name;
        private int legs;
    ​
        public String getName() {
            return name;
        }
    ​
        public int getLegs() {
            return legs;
        }
    ​
        public void setName(String n) {
            name = n;
        }
        
        public void setLegs(int l) {
            if (l >= 0 && l % 2 == 0) {
                legs = l;
            }
        }
    ​
        public void move() {
            System.out.println("我叫" + name + ",我用" + legs + "条腿跑步!");
        }
    }
    ​
    //*********************************************
    ​
    public class AnimalTest {
        public static void main(String[] args) {
            Animal pig = new Animal();
            pig.setName("佩奇");
            pig.setLegs(-3);
            pig.move();
        }
    }
  • 以上是封装性的体现之一,但是它并不等同于封装性!!!这点需要牢记,关于封装性的体现还有其他方面等等。比如我们将某个方法添加上private关键字,那么该方法只作为内部使用,并不会对外暴露。你是好人并不等同于你真的好!

封装性的体现,需要权限修饰符来配合。

  • Java规定了4种权限(从小到大排列):private、缺省、protected、public

  • 4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类

  • 对于class的权限修饰只可以用public和default(缺省), public类可以在任意地方被访问,default类只可以被同一个包内部的类访问。

  • 开心一笑

A man and woman are in a computer programming lecture. The man touches the woman's hand.
"Hey!" she says. "Those are private!"
The man says, "But we're in the same class!"

4. 变量访问原则和this关键字

  • 对于先前标识符的使用我们还是希望可以见明知意:我们将setName与setLegs方法中的形参都变成name和legs,我们发现方法体中的赋值出现了问题。产生问题的原因就是因为变量的访问机制。

  • 变量访问原则:

    • 就近原则:当在访问某个变量名称的时候,会先寻找最近的该变量名称的定义,如果寻找到了,就使用该变量,如果没有找到,才到更远的位置寻找该变量名称的定义。

    • 当局部变量和成员变量同名的时候,一定是先使用局部位置定义的变量,如果没有,才会使用成员位置定义的变量。

  • this关键字:

    • 表示当前类型当前对象的引用:哪个来调用this所在的方法,this就代表哪个对象

      • 作用:用于区分局部变量和成员变量同名的情况。使用this.属性名称的一定是成员变量,没有使用this.的变量,根据就近原则来确定使用哪个变量。

    • 示例:

      public class Animal {
          private String name;
          private int legs;
      ​
          public String getName() {
              return name;
          }
      ​
          public int getLegs() {
              return legs;
          }
      ​
          public void setName(String name) {
              this.name = name;
          }
      ​
          public void setLegs(int legs) {
              if (legs >= 0 && legs % 2 == 0) {
                  this.legs = legs;
              }
          }
      ​
          public void move() {
              System.out.println("我叫" + name + ",我用" + legs + "条腿跑步!");
          }
      }
       /*
           * this关键字的使用
           * 1.this可以用来修饰:属性、方法、构造器
           *
           * 2.this修饰属性和方法:
           *   this理解为当前对象或者当前正在创建的对象
           *
           *   2.1 在类的方法中,我们可以使用"this.属性"或者"this.方法"的形式,调用当前对象的属性或者方法。
           *   但是,通常情况下,我们都选择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须
           *   显式的使用"this.变量"的方式,表明此变量是属性,而非形参。
           *
           *  2.2 在类的构造器中,我们可以使用"this.属性"或者"this.方法"的形式,调用当前正在创建对象的属性或者方               法。
           *   但是,通常情况下,我们都选择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须
           *   显式的使用"this.变量"的方式,表明此变量是属性,而非形参。
           * */

5. 构造方法

5.1 构造方法概述

  • 构造方法:构造函数,构造器,Constructor

  • 作用:用于给对象中的成员变量赋值。因为在创建对象的同时,JVM会自动调用构造方法,等对象创建完成的时候,对象中的成员变量就已经有指定的值了。

  • 语法结构:

    修饰符 方法名称(参数列表) {
        方法体
    }
  • 构造方法说明:

    • 构造方法的方法名称,必须和类名一模一样,连大小写都一样

    • 构造方法没有返回值类型,连void也没有

    • 构造方法没有return语句,如果一定需要return语句,就写一个return;return后面不能跟内容,加return没有实际的意义

    • 构造方法不需要手动调用,由JVM在创建对象的时候自动调用

    • 对象本身不能调用构造方法

    • 构造方法只调用一次

  • 示例:

    public class Person {
        private String name;
        private int age;
        private String gender;
    ​
        public Person() {
            System.out.println("我被执行了");
        }
    }
    .......
      
      public class PersonTest {
        public static void main(String[] args) {
            Person person = new Person();
        }
    }
    // 在创建对象的同时,会自动调用构造方法
  • 注意事项:

    • 构造方法可以是有参数的,也可以是没有参数的。

      • 如果是没有参数的构造方法,外界无需传入任何的参数值,只能给成员变量赋固定值或者不赋值。

      • 如果是有参数的构造方法,外界在调用构造方法的时候,需要传入实际的参数值,通常用于赋值给成员变量。

    • 如果在类中没有定义任何的构造方法,那么系统会自动提供一个空参构造(空实现)。

    • 如果在类中手动定义了一个构造方法(无论是空参还是有参),系统都不再会提供任何的构造方法。

    • 构造方法的重载:构造方法都在同一个类中,构造方法的方法名称都和类名一致,参数列表不同,没有返回值类型,一般在类中,既需要空参构造,也需要有参构造,都手动定义出来。

    • 示例:

        public class Person {
          private String name;
          private int age;
          private String gender;
      ​
          public Person(String name, int age, String gender) {
              this.name = name;
              this.age = age;
              this.gender = gender;
          }
      ​
          public Person() {
          }
      ​
          public String getName() {
              return name;
          }
      ​
          public void setName(String name) {
              this.name = name;
          }
      ​
          public int getAge() {
              return age;
          }
      ​
          public void setAge(int age) {
              this.age = age;
          }
      ​
          public String getGender() {
              return gender;
          }
      ​
          public void setGender(String gender) {
              this.gender = gender;
          }
      ​
          @Override
          public String toString() {
              return "Person{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      ", gender='" + gender + '\'' +
                      '}';
          }
      }
      ​
      public class PersonTest {
          public static void main(String[] args) {
              Person person = new Person("zhangsan", 18, "男");
              System.out.println(person);
          }
      }
       /*****************************************************/
      我们也可以给set方法进行改造,让它返回对应的类型,改造方式如下:
      public Person setName(String name) {
              this.name = name;
              return this;
          }
      ​
       public Person setAge(int age) {
              this.age = age;
              return this;
          }
      ​
       public Person setGender(String gender) {
              this.gender = gender;
              return this;
          }
       创建对象时可以使用链式编程的方式
         
        Person person = new Person().setName("lisi").setAge(20).setGender("男");

5.2 构造方法和set方法的比较

  • 共同点:构造方法和set方法都是用于给成员变量赋值。不希望外界直接访问私有成员变量,通过构造方法或者set方法,间接的访问私有变量。

  • 区别:

    • 构造方法:构造方法是在创建对象的同时,由JVM自动调用执行,用于给属性赋值,只能执行一次。

    • set方法:set方法是在创建对象之后,由对象手动调用执行,用于给属性修改值,可以调用多次。

  • 使用场景比较:

    • 构造方法:只能在创建对象的时候被自动调用一次,代码更加简洁。一旦对象创建成功,就不能继续使用构造方法修改成员变量的值。

    • set方法:一般set方法使用更加灵活,使用更加频繁。

  • 总结:属性赋值的先后顺序

    • 默认初始化

    • 显示初始化

    • 构造器初始化

    • 通过"对象.方法"或者"对象.属性"的方式,赋值

    • 最终结果由最后执行的方式决定

6. 静态

6.1 静态概述

  • 静态:static关键字,静态、静止的。静态变量不会随着对象的变化而变化。

  • 加载时机:

    • 随着类的加载而加载。

    • 静态变量随着类的加载进方法区,就直接在静态区给开辟了存储静态变量的内存空间。

  • 静态变量优先于对象而存在。

  • 静态变量被所有该类对象所共享。

  • 代码层面:可以使用类名直接调用,不需要使用对象名称。在不创建对象的前提下,仍然可以使用这个静态变量。

  • 强烈建议使用类名来访问。

6.2 静态效果

  • 不使用静态:

    • 现象:如果某个类型的所有对象,都具有一个相同的属性值,比如2个对象的country都是中国; 那么这个属性值就没有必要在所有对象中,都存储一份。

    • 缺点:浪费内存空间;维护难度大,一旦需要修改,就得修改所有的对象。

    • 示例:

      public class StaticDemo {
          private String username;
          private String country;
      ​
          public String getUsername() {
              return username;
          }
      ​
          public void setUsername(String username) {
              this.username = username;
          }
      ​
          public String getCountry() {
              return country;
          }
      ​
          public void setCountry(String country) {
              this.country = country;
          }
      ​
          public static void main(String[] args) {
              StaticDemo st1 = new StaticDemo();
              st1.setUsername("zs");
              st1.setCountry("中国");
              StaticDemo st2 = new StaticDemo();
              st1.setUsername("lisi");
              st1.setCountry("中国");
      ​
              st1.setCountry("东三省");
              st2.setCountry("东三省");
          }
      }
  • 使用静态:

    • 现象:如果某个类型的所有对象,都具有一个相同的属性值,那么就在这个属性的定 义上,加一个static静态关键字。让该变量存储在方法区字节码的静态区中,避免了所有对象都存储相 同数据的问题,节省了内存空间,将来维护容易(只需要修改一次)。

    • 示例:

      public class StaticDemo {
          private String username;
          private static String country;
      ​
          public String getUsername() {
              return username;
          }
      ​
          public void setUsername(String username) {
              this.username = username;
          }
      ​
          public static String getCountry() {
              return country;
          }
      ​
          public static void setCountry(String country) {
              StaticDemo.country = country;
          }
      ​
          public static void main(String[] args) {
              StaticDemo st1 = new StaticDemo();
              st1.setUsername("zs");
              st1.setCountry("中国");
              StaticDemo st2 = new StaticDemo();
              st1.setUsername("lisi");
              System.out.println(st2.getCountry());
              st2.setCountry("东三省");
              System.out.println(st1.getCountry());
          }
      }
  • 注意事项:

    • 静态方法:在方法声明上,加上了static关键字的方法,就是静态方法。

    • 静态方法不能访问非静态的变量:静态方法本身可以在没有创建对象的时候调用;非静态的变量只有在对象创建之后才存在。如果静态方法可以访问非静态的变量,那么就相当于在对象创建之前,就访问了对象创建之后的数据。明显不合理。

    • 静态方法不能访问非静态的方法:静态方法可以在没有创建对象的时候调用;非静态的方法可以访问非静态的变量。如果静态方法可以访问非静态的方法,就相当于静态方法间接的访问了非静态的变量,和第2点矛盾。

    • 静态方法中不能存在this关键字:this关键字表示本类当前对象。静态方法可以在对象创建之前调用。如果静态方法可以访问 this关键字,相当于在创建对象之前,就使用了对象本身。矛盾。

    • 总结:静态资源不能访问非静态资源

6.3 静态变量和非静态变量的区别

  • 概念上,所属不同:

    • 非静态变量属于对象。

    • 静态变量属于类,类变量。

  • 内存空间不同,存储位置不同:

    • 非静态变量属于对象,所以存储在堆内存中。

    • 静态变量属于类,存储在方法区的静态区中。

  • 内存时间不同,生命周期不同:

    • 非静态变量属于对象,所以生命周期和对象相同,随着对象的创建而存在,随着对象的消失而消失。

    • 静态变量属于类,所以生命周期和类相同,随着类的加载。

  • 访问方式不同:

    • 非静态变量只能使用对象名访问。

    • 静态变量既可以使用对象访问,也可以通过类名访问(强烈建议使用类名访问):

      • 类名.静态变量名

      • 类名.静态方法名()

6.4 工具类

  • 工具类:在一个类中,没有维护什么数据,没有任何的成员变量,相当于是一个工具。类中就都是一些静态方法,快速的解决一些常见的问题。

  • 方法都是静态的,不需要创建对象;创建对象会浪费系统资源。希望控制不让创建对象(方式:使用构造方法私有化)。

  • 示例:数组工具类

    public class ArraysUtils {
        //    构造方法私有化,保证类不会被创建对象
        private ArraysUtils() {
        }
    ​
        // 1、循环打印数组
        public static void print(int[] arr) {
            for (int i = 0; i < arr.length; i++) {
                System.out.print(arr[i] + ",");
            }
        }
    ​
        // 2、循环打印数组,一行打印指定个数的元素
        public static void print(int[] arr, int number) {
            for (int i = 0; i < arr.length; i++) {
                System.out.print(arr[i] + ",");
                if ((i + 1) % number == 0) {
                    // 换一行
                    System.out.println();
                }
            }
        }
    ​
        // 3、将数组转成[元素1,元素2...]这种格式的字符串
        public static String formatPrint(int[] arr) {
            if (arr == null)
                return "null";
            // 如果iMax是-1 意味着数组长度是0
            int iMax = arr.length - 1;
            if (iMax == -1)
                return "数组长度为0";
            StringBuilder b = new StringBuilder();
            b.append('[');// 在最前方追加一个[
            for (int i = 0; ; i++) {
                b.append(arr[i]);
                // 满足条件 就退出
                if (i == iMax) {
                    return b.append(']').toString();
                }
                b.append(", ");
            }
        }
    ​
        // 4、将数组冒泡\或者其他算法排序(直接将原数组排序)
        public static void sort(int[] arr) {
            for (int i = 0; i < arr.length - 1; i++) {
                for (int j = 0; j < arr.length - 1 - i; j++) {
                    if (arr[j] > arr[j + 1]) {
                        int desk = arr[j];
                        arr[j] = arr[j + 1];
                        arr[j + 1] = desk;
                    }
                }
            }
        }
    ​
        // 5、将数组冒泡\或者其他算法排序(不允许排形参的数组,需要直接返回一个排好序的新数组)
        public static int[] sortNew(int[] arr) {
            int arrNew[] = new int[arr.length];
            // 把源数组 内容赋值一份给 arrNew
            // arrNew = Arrays.copyOf(arr, arr.length);
            for (int i = 0; i < arrNew.length; i++) {
                arrNew[i] = arr[i];
            }
    ​
            for (int i = 0; i < arrNew.length - 1; i++) {
                for (int j = 0; j < arrNew.length - 1 - i; j++) {
                    if (arrNew[j] > arrNew[j + 1]) {
                        int desk = arrNew[j];
                        arrNew[j] = arrNew[j + 1];
                        arrNew[j + 1] = desk;
                    }
                }
            }
            return arrNew;
        }
    ​
        // 6、比较两个数组的所有元素是否完全一致
        public static boolean isEquals(int[] arr1, int[] arr2) {
            // 地址相同 里面的数据肯定相同
            if (arr1 == arr2)
                return true;
            if (arr1 == null || arr2 == null)
                return false;
    ​
            int length = arr1.length;
            if (arr2.length != length)
                return false;
    ​
            for (int i = 0; i < length; i++)
                if (arr1[i] != arr2[i])
                    return false;
    ​
            return true;
        }
    ​
        // 7、计算数组的平均值
        public static double avg(int[] arr) {
            double sum = 0;
            for (int i = 0; i < arr.length; i++) {
                sum += arr[i];
            }
            return sum / arr.length;
        }
    ​
        // 8、计算数组的最大值
        public static int max(int[] arr) {
            // 数组第一个 为最大值
            int maxnumber = arr[0];
            for (int i = 1; i < arr.length; i++) {
                if (maxnumber < arr[i]) {
                    maxnumber = arr[i];
                }
            }
            return maxnumber;
        }
    ​
        // 9、计算数组的最小值
        public static int min(int[] arr) {
            // 数组第一个 为最小值
            int minnumber = arr[0];
            for (int i = 0; i < arr.length; i++) {
                if (minnumber > arr[i]) {
                    minnumber = arr[i];
                }
            }
            return minnumber;
        }
    ​
        // 10、将一个数组的所有元素都反转(比如{5,4,8}变成{8,4,5})
        public static int[] reverse(int[] arr) {
            int[] arrNew = new int[arr.length];
            for (int x = 0; x < arr.length; x++) {
                arrNew[x] = arr[arr.length - 1 - x];
            }
            return arrNew;
        }
    ​
        // 11、判断一个指定的数值在数组中是否存在
        public static boolean isExits(int[] arr, int number) {
            boolean isTrue = false;
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] == number) {
                    return true;
                }
            }
            // 如果不存在
            return false;
        }
    }

7. 代码块

7.1 概述

  • 使用大括号包起来的一段代码。放在不同的位置,有不同的名称,有不同的作用,有不同的执行时机。

  • 分类:

    • 局部代码块

    • 构造代码块

    • 静态代码块

    • 同步代码块(多线程中会遇到)

7.2 局部代码块

  • 格式:使用大括号包起来的一段代码

  • 位置:方法中

  • 作用:

    • 限定变量的生命周期,作用域只是在大括号内部。

    • 在局部代码块中【声明】的变量,只能在局部代码块的范围内使用,一旦出了局部代码块的大括号,变量就不能继续使用了。

    • 某个变量一旦不能使用了,就会被回收,节省内存空间。

  • 注意事项:

    • 局部代码块内声明的变量,会减少变量的生命周期,局部代码块执行完毕后,就无法继续使用变量。

    • 局部代码块外声明的变量,局部代码块执行完毕后,仍可以继续使用变量。

  • 示例:

    public class Demo {
        public static void main(String[] args) {
            int a = 123;
            {
                int b = 456;
                System.out.println(a);
                System.out.println(b);
            }
            System.out.println(a);
        }
    }

7.3 构造代码块

  • 格式:使用大括号包起来的一段代码

  • 位置:类中方法外

  • 作用:

    • 用于给成员变量初始化赋值。

    • 在创建对象时执行部分操作。

  • 构造代码块的执行说明:

    • 在创建对象的时候执行,由 JVM 默认调用,用于给对象的属性赋值。

    • 在构造方法执行之前执行。

    • 任意一个构造方法执行之前,都会执行一次构造代码块的内容。

    • 如果每个构造方法都会执行的内容,请提取到构造代码块中执行。

  • 示例:

    public class Demo {
        {
            System.out.println("我是一个构造代码块");
        }
    ​
        public static void main(String[] args) {
            Demo demo = new Demo();
        }
    }

7.4 静态代码块

  • 格式:

    static {
        静态代码块的内容
    }
  • 位置:类中方法外

  • 作用:

    • 用于给静态的成员变量初始化赋值

    • 用于执行那些只需要执行一次的代码,例如驱动加载等

  • 执行特点:

    • 随着类的加载而执行

    • 类只加载一次,所以静态代码块只执行一次

    • 执行的时机最早:早于所有的对象相关内容,比main方法执行的早

  • 示例:

    public class Demo {
        static {
            System.out.println("说我是静态代码块");
        }
    ​
        {
            System.out.println("我是构造代码块");
        }
    ​
        public static void main(String[] args) {
            System.out.println("我是main方法");
            Demo demo = new Demo();
        }
    }
  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

White-Camellia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值