Java面向对象之——封装

简介: Java面向对象之——封装

前言

我们知道Java是一门面向对象的语言,对于面向对象的程序有三大特性:封装、继承、多态。我们上期了解了类和对象,在类和对象阶段,主要研究的就是封装特性。

一、封装的概念

举个例子:我要看电视,只需要按一下开关和换台就可以了。有必要了解电视机内部的结构吗?有必要碰碰显像管吗?制造厂家为了方便我们使用电视,把复杂的内部细节全部封装起来,只给我们暴露简单的接口。让用户可以与电视机进行交互即可。


二、何为封装?

何为封装呢?简单来说就是套壳屏蔽细节。封装的理念是高内聚、低耦合。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合是仅暴露少量的方法给外部使用,尽量方便外部调用。

要了解封装就需要了解访问权限,要了解访问权限需要了解包,那么下面我们就先来讲一下——Java包


三、封装拓展——包

🍑1、包的概念

在面向对象体系中,提出了一个软件包的概念,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式。包(package)相当于文件夹对于文件的作用。用于管理类、用于解决类的重命名问题。

🍑2、导入包中的类

Java 中已经提供了很多现成的类供我们使用。例如Date类:可以使用 java.util.Date; 表示导入 java.util 这个包中的 Date类。Arrays类:可以使用import java.util.Arrays;表示导入java.ulil这个包中的Arrays类。

📝方式1:写全路径导入

public class Test3 {
    public static void main(String[] args) {
        java.util.Date date=new java.util.Date();
    }
}

虽然这样也可以导入,但是这种写法比较冗余,不建议。

📝方式2:使用import导入

import java.util.Date;
public class Test3 {
    public static void main(String[] args) {
        Date date=new Date();
    }
}

📝方式3:使用 import 包路径. * 导入包中的其他类,

需要注意的是:这种导入方式不是将包中的所有类都导入进来,而是需要谁就导入谁。

import java.util.*;
public class Test3 {
    public static void main(String[] args) {
        Date date = new Date();
    }
}

虽然这样也可以导入包中的类,但是我们更建议显式的指定要导入的类名,指定类名可以使导入类更清楚明确。

当我们这样导入,也可能出现冲突的情况。如:java.util包和java.sql包下都有Date类,如果我们使用这种导入方式,会导致编译器不知道应该从哪个包中导入Date类,将会报错。

📝方式4:使用import static导入包中静态的方法和字段。

这种导入方式了解即可,不建议使用。

import static java.lang.Math.*;
public class Test3 {
    public static void main(String[] args) {
        double x = 30;
        double y = 40;

// double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        //省略类名Math
        double result = sqrt(pow(x, 2) + pow(y, 2));
        System.out.println(result);
    }
}

🍑3、自定义包

基本规则

  • 在文件的最上方加上一个 package 语句指定该代码在哪个包中.
  • 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.zijie.demo1 ).
  • 包名要和代码路径相匹配. 例如创建 com.zijie.demo1 的包, 那么会存在一个对应的路径com/zijie/demo1 来存储代码.
  • 如果一个类没有 package 语句, 则该类被放到一个默认包中.

注: 如果在源文件中没有定义包,那么类、接口、枚举和注释类型文件将会被放进一个无名的包中,也称为默认包。学了包之后,写项目时都要加包,尽量不要使用默认包!

下面就演示一下在IDEA中创建包:

🍑4、常见的包

  1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
  2. java.lang.reflect:java 反射编程包;
  3. java.net:进行网络编程开发包。
  4. java.sql:进行数据库开发的支持包。
  5. java.util:是java提供的工具程序包。(集合类等) 非常重要
  6. java.io:I/O编程开发包。

四、访问限定符

Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:

注解:

  1. protected主要是用在继承中,继承部分详细介绍
  2. default权限指:什么都不写时的默认权限
  3. 访问权限除了可以限定类中成员的可见性,也可以控制类的可见性

目前来说,我们就先介绍public、private、默认修饰符:

🍑1、public修饰符

public修饰的成员在哪里都能访问,它的权限是四种修饰符中最大的。这里就不过多展示了。

🍑2、private修饰符

🍑3、默认修饰符(缺省)

🍑4、protected修饰符

------------------ (继承部分讲解)------------------

五、再谈封装

上面提到:封装是对类细节的隐藏,提供公开接口来进行和数据的交互。通过搭配上述的修饰符这一特点体现的更为明显。

📝如:对学生类使用private进行封装

class Student {
    //属性
    private String name;
    private int age;
    //方法
    public void show() {
        System.out.println("姓名:"+name+" 年龄:"+age);
    }

    //构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //交互接口
    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 class Test2 {
    public static void main(String[] args) {
        Student student =new Student("张三",20);
        student.show();

        student.setName("李四");
        student.setAge(18);
        System.out.println(student.getName());
        System.out.println(student.getAge());
    }
}

通过这个例子,我们或许可以看到到封装在类中的体现,通过使用private对Student中成员进行隐藏,在提供一些对外的接口如,setName、getName等实现对外的交互。如需要对封装的成员进行操作,调用接口方法即可。


六、static成员

🍑1、再谈学生类——static的引入

学生有属性:姓名,年龄,身高,体重……这里仅以姓名年龄属性举例:

class Student {
    //属性
    public String name;
    public int age;

    //构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class Test2 {
    public static void main(String[] args) {
        Student student1=new Student("张三",17);
        Student student2=new Student("李四",18);
        Student student3=new Student("王五",19);
    }
}

现在假设这三个同学是来自于同一个班级的,那么我们能否加一个普通成员变量来统一保存学生的班级信息呢?答案显然是不行的。为了实现这种功能,于是引出了static关键字。

之前在Student类中定义的成员变量,每个对象中都会包含一份(称之为实例变量),因为需要使用这些信息来描述具体的学生。而现在要表示学生上课的教室,这个教室的属性并不需要每个学生对象中都存储一份,而是需要让所有的学生来共享。在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所共享的。

🍑2、static修饰成员变量

static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。

学习了static关键字之后我们可将成员变量分为:

1、静态成员变量(类变量)

2、非静态成员变量(普通成员变量)

我们再回到之前的问题,现在我们想要统一保存学生的班级信息只需要添加一个static修饰的成员变量即可:

class Student {
    //属性
    public String name;
    public int age;
    public static String classRome="软件1班";  
    //构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Student student1=new Student("张三",17);
        Student student2=new Student("李四",18);
        Student student3=new Student("王五",19);
        //使用类和对象都可以访问static修饰的成员变量
        System.out.println(Student.classRome);
        System.out.println(student1.classRome);
    }
}

此时我么再次调试:

结合代码功能、运行和调试足以说明【静态成员变量】具有以下特性:

  1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
  2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
  3. 类变量存储在方法区当中
  4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)

🍑3、static修饰成员方法

一般类中的数据成员都设置为private,而成员方法设置为public,那设置之后,Student类中classRoom属性如何在类外访问呢?这里就引出了静态方法。Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。学习了static关键字之后我们将成员方法分为:

1、静态成员方法(类方法)

2、非静态成员方法(普通成员方法)

class Student {
    //属性
    private String name;
    private int age;
    private static String classRome="软件1班";
    
    public static String getClassRome() {
        return classRome;
    }

    public static void setClassRome(String classRome) {
        Student.classRome = classRome;
    }
}

public class Test2 {
    //使用静态方法访问静态变量
    public static void main(String[] args) {
        System.out.println(Student.getClassRome());
        Student.setClassRome("软件2班");
        System.out.println(Student.getClassRome());
    }
}

🍑4、静态方法特性

  1. 不属于某个具体的对象,是类方法
  2. 可以通过对象调用,也可以通过类名。静态方法名(…)方式调用,更推荐使用后者。
  3. 不能在静态方法中直接访问任何非静态成员变量,无法传递this引用。
class Student {
   public int age;
}

public class Test2 {
    //在静态方法中不能直接访问非静态方法
    public static void main(String[] args) {
        //System.out.println(this.age);//不能使用this引用
    }
}
  1. 静态方法中不能直接调用任何非静态方法,因为非静态方法有this参数,在静态方法中无法传递this引用。
class Student {
    public void test() {
        System.out.println("test()");
    }
}
public class Test2 {
    //在静态方法中不能直接访问非静态方法
    public static void main(String[] args) {
//        Student.test();//错误
        //在静态方法中访问非静态方法只能new对象
          Student stu1=new Student();
          stu1.test();
    }
}
  1. 普通成员方法内部是可以使用静态的成员方法和成员变量的。
class Student {
   private static int age=10;
   private static String name="张三";
   
   private static void print() {
       System.out.println("test()");
   }
   
   public void test() {//这样写是可以的,不会报错
       Student.age=10;
       Student.name="李四";
       Student.print();
   }
}

🍑5、static成员变量初始化

(1)默认初始化

class Student {
   public static int age;//默认赋值为0
   public static String name;//默认赋值为null
}

同普通成员变量,如果静态成员变量创建时不赋初值,将被赋默认值。赋默认值规则同普通成员变量。

(2)就地初始化

class Student {
   public static int age=10;
   public static String name="张三";
}

像上面这种,在定义时直接给出初始值的赋值方式为就地初始化。

(3)使用构造方法初始化

class Student {
   public static int age;
   public static String name;

   public Student(int a,String n) {
       age=a;
       name=n;
   }
}

虽然使用构造方法也可以给静态变量赋值,但是静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性,而静态变量是所有对象共享的,每次new对象时都把static变量改了是不合适的。

(4)使用Setter方法

class Student {
   public static int age;
   public static String name;
   
   public static void setAge(int age) {
       Student.age = age;
   }

   public static void setName(String name) {
       Student.name = name;
   }

(5)使用静态代码块初始化

静态代码块是什么?我们接着往下看


七、代码块

🍑1、代码块概念以及分类

使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:普通代码块构造块静态块同步代码块(后续讲解多线程部分再谈)

🍑2、普通代码块

普通代码块,是指定义在方法中的代码块。

//这是一个main方法
    public static void main(String[] args) {
        { //直接使用{}定义,普通方法块
            int x = 10;
            System.out.println("x1 = " + x);
        }
    }

🍑3、非静态代码块

非静态代码块也叫实例代码块、构造块,是定义在类中的代码块(不加修饰符)。构造代码块一般用于初始化非静态实例成员变量。

class Student {
   public int age;
   public String name;

    //构造代码块,实例化成员变量
   {
       age=10;
       name="李四";
   }
    public void show() {
        System.out.println("年龄:"+age+" 姓名:"+name);
    }
}
public class Test2 {
    public static void main(String[] args) {
        Student student1=new Student();
        student1.show();
  }
}

补充:

  1. 如果都是非静态的,那么定义顺序谁在后最后赋值结果就是哪个值。
  2. 可以简单认为,编译器编译好代码之后会把非静态代码块的东西放到构造方法的最前面。

🍑4、静态代码块

使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。

class Student {
    private String name;
    private String gender;
    private static String classRoom;

    // 静态代码块
    static {
        classRoom = "软件1班";
        System.out.println("I am static init()!");
    }

    public static void main(String[] args) {
        //静态代码块的加载不依赖于对象
        System.out.println(Student.classRoom);
        //静态代码块只执行1次
        Student s1 = new Student();
        Student s2 = new Student();
    }
}

注意事项:

  1. 静态代码块不管生成多少个对象,其只会执行一次
  2. 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
  3. 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
  4. 实例代码块只有在创建对象时才会执行

🍑5、执行顺序

1. 静态代码块先执行,并且只执行一次,在类加载阶段执行。

2. 当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后执行构造方法。

class Demo {
    public String name;
    public int age;
    //构造方法
    public Demo(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("构造方法执行");
    }
    //实例代码块
    {
        System.out.println("实例代码块执行");
    }
    //静态代码块
    static {
        System.out.println("静态代码块执行");
    }
}

public class Test {
    public static void main(String[] args) {
        Demo demo=new Demo("张三",20);
        System.out.println("===========分隔线===========");
        Demo demo2=new Demo("李四",18);
    }
}


八、对象的打印

class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {
        Student student=new Student("张三",18);
        System.out.println(student);
    }
}

当我们使用println打印时,如果直接将student引用作为参数传入println(student),我们能得到引用指向对象中的属性值吗?答案是不能的。我们将得到如下图的数值:

如果想要println默认打印对象中的属性该如何处理呢?答案:重写toString方法即可。也就是说对于你想输出一个对象的引用的值的时候,如果你没有自己写一个toString方法,那么就会调用Object这个类的方法,如果自己实现了,就调用自己的:

class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

  //String方法重写
    @Override//这个是注解,注解有很多种,这只是其中1种
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", gender='" + age + '\'' +
                '}';
    }
    
    public static void main(String[] args) {
        Student student=new Student("张三",18);
        System.out.println(student);
    }
}

补充:对于上述提到的,方法重写以及Object的相关知识后期博客会详细介绍。


九、拓展:不学后悔的IDEA技巧

🍑1、快速生成构造方法

🍑2、快速生成Setter和Getter接口方法

🍑3、快速生成重写

IDEA中也有一个快速重写toString的技巧

其实使用IDEA还可以实现很多便捷的操作,大家可以在使用中慢慢摸索。




总结

本期主要探讨了面向对象三大特性之一的封装,重点介绍了Java包的概念、引入了静态属性static、详细介绍了Java中的三种代码块、最后拓展了一下IDEA中的小技巧,本章篇幅较长,笔者尽量做到句句重点,如有不足,敬请斧正。我们下期再见!


相关文章
|
1天前
|
Java 开发者
Java 面向对象新视界:揭秘子类如何“继承”父类精华,再添“创新”之笔
【6月更文挑战第16天】在Java的面向对象世界,子类继承父类的特性,如`Circle`继承`Shape`,展示“is-a”关系。子类不仅保留父类的`color`和`display`方法,还添加了`radius`属性及定制的显示逻辑。这种继承与创新允许代码复用,增强灵活性和可扩展性,使得构建复杂系统变得更加高效和模块化。通过持续的继承与定制,开发者能构建出一系列独具特色的类,充分展现面向对象编程的力量。
|
1天前
|
Java
Java 面向对象新篇章:子类如何“站在巨人肩膀上”,继承与创新并存!
【6月更文挑战第16天】Java 中的子类继承父类,实现代码复用和扩展。子类自动获得父类属性和方法,减少冗余,保证一致性。通过示例展示了`Circle`类如何继承`Shape`类并添加新特性。子类不仅能继承,还能创新,如`Circle`类增加计算面积方法。这种继承与创新结合,构成Java面向对象编程的核心,支持构建灵活、高效的软件系统。
|
1天前
|
安全 Java
Java 面向对象之旅:封装——让代码更加“接地气”的秘诀。
【6月更文挑战第16天】**Java面向对象的封装秘籍:**将数据和操作打包成类,如`Student`和`Car`,隐藏内部详情,只通过`get/set`方法交互。封装提升代码清晰度,便于管理和保护安全性,就像整理工具箱,让每个功能一目了然,操作自如。
|
1天前
|
安全 Java 数据安全/隐私保护
一探 Java 封装究竟:为何它让代码更加“高大上”?
【6月更文挑战第16天】Java中的封装如城堡般迷人,它确保数据安全(如`UserInfo`类的私有属性),增强代码结构(如`Order`类的操作封装),并提升复用性与扩展性(如`Shape`类的抽象设计)。封装是打造高质量、易维护代码的关键,让代码既安全又高效。
|
1天前
|
Java 数据安全/隐私保护
Java 封装:打破传统,创新你的编程思维!
【6月更文挑战第16天】Java中的封装是将数据和操作数据的方法封装在类中,隐藏内部细节,通过公共接口交互。这样保证了数据安全,降低了耦合度,便于验证(如`Shape`类中构造函数的类型检查)和控制(如`setType`方法中添加额外操作)。封装使代码更清晰、可维护,鼓励创新编程思维。
|
1天前
|
Java 开发者
那些年,我们追过的 Java 封装:从新手到专家的必经之路
【6月更文挑战第16天】Java初学者到专家的旅程中,封装是关键一环。封装隐藏实现细节,提供简洁接口,保护数据并确保代码整洁、可维护。以`Person`类为例,私有变量与公共方法展示了封装原则,助力团队协作和问题解决,成为编程路上的宝贵经验。封装,从新手到高手的不解之缘,持续塑造更高效、可靠的软件。
|
1天前
|
存储 安全 Java
深入探讨 Java 封装机制:为何它是面向对象编程的核心?
【6月更文挑战第16天】Java的封装是OOP核心,它将数据和操作数据的方法打包在类中,隐藏实现细节并提供公共接口。例如,`Student`类封装了私有属性`name`和`age`,通过`get/set`方法安全访问。封装提升代码稳定性、可维护性和复用性,防止外部直接修改导致的错误,确保数据安全。它是面向对象编程优于传统编程的关键,促进高效、可靠的开发。
|
2天前
|
安全 Java
Java 面向对象之旅:在封装的港湾中,找到代码的安宁。
【6月更文挑战第15天】封装是Java面向对象的核心,它保护了类的内部数据,如`Book`和`Student`类中的属性。通过设定私有成员和公共方法,代码更有序,防止直接访问导致的混乱。封装提供了一种控制数据交互的方式,确保安全,如`setGpa()`方法验证输入。这使得代码结构清晰,如同港湾中的船只,井然有序,赋予编程过程美感和安全感。在面向对象的旅程中,封装是我们的庇护所,助力我们构建更美好的程序世界。
|
2天前
|
Java
谁说 Java 封装很难?跟我学,秒变编程大神!
【6月更文挑战第15天】Java封装,就是将数据和相关操作打包,保护数据免受非法访问。比如`SuperHero`类,它的属性用`private`隐藏,通过`get/set`方法控制访问。这样提高了数据安全性和稳定性。就像超级英雄的超能力,不能随意使用。掌握封装,编程就变得更简单,助你成为Java大神!开始征服代码高峰吧!💪🎉
|
2天前
|
安全 Java 数据安全/隐私保护
Java 封装:对象的“面纱”下,隐藏着怎样的世界?
【6月更文挑战第15天】Java封装是对象的隐私保护,它隐藏类的内部细节并设定访问接口。`Person`类展示了如何用`private`字段和公共getter/setter方法实现封装,确保数据安全性和代码可维护性。例如,`BankAccount`类封装了余额,只允许通过`deposit`、`withdraw`和`getBalance`方法操作。封装减少了外部干扰,增强安全性,提高可扩展性,是Java面向对象编程的核心原则之一。