Java封装

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

  • image-20230202234004982

  • 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();
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Gao_xu_sheng

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

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

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

打赏作者

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

抵扣说明:

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

余额充值