封装的概述
对外部隐藏内部细节
1、封装的目的是隐藏对象的内部状态和实现细节,只暴露出一个可以被外界访问和操作的接口。通过将类的属性设置为私有(private),防止外部直接访问和修改这些属性。
2、好处:高内聚低耦合(面向对象设计的最高原则)
(1)隐藏事物的实现细节降低使用难度
(2)提高了代码的复用性
(3)提高了安全性
3、封装的原则
(1)隐藏事物的属性
(2)隐藏事物的实现细节
(3)对外提供公开的访问方式
public class Person {
// 私有属性,外部无法直接访问
private String name;
private int age;
// 构造器,用于创建对象时初始化属性
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 公共方法,用于获取私有属性name的值
public String getName() {
return name;
}
// 公共方法,用于设置私有属性name的值
public void setName(String name) {
this.name = name;
}
// 公共方法,用于获取私有属性age的值
public int getAge() {
return age;
}
// 公共方法,用于设置私有属性age的值
public void setAge(int age) {
if (age > 0) { // 可以添加逻辑来验证数据
this.age = age;
}
}
}
// 使用封装的类
public class Main {
public static void main(String[] args) {
Person person = new Person("Alice", 30);
System.out.println(person.getName()); // 使用公共方法访问私有属性
person.setName("Bob"); // 使用公共方法修改私有属性
person.setAge(25); // 使用公共方法设置经过验证的年龄
}
}
Person类有两个私有属性:name和age。这些属性不能被外部直接访问,而是通过公共的getter和setter方法来访问和修改。这种方式不仅隐藏了实现细节,还允许在访问和修改属性时加入额外的逻辑,比如验证数据的有效性。
封装是OOP中实现数据安全和降低系统各部分之间耦合度的重要手段。高内聚低耦合
Java中的权限修饰符主要包括private、public和protected,这些修饰符控制着对类和类的成员变量以及成员方法的访问。
一、private关键字(私有的)
private修饰符如果修饰成员变量和成员方法,则该成员只能在本类中使用,在子类中是不可见的,对其他包的类也是不可见的。
private修饰类时,这个类将隐藏其中所有的数据,防止用户直接访问该类。
二、public关键字
public修饰符修饰成员变量和成员方法时,那么除了可以在本类访问使用这些数据,还可以在子类和其他包的类中使用。
public 修饰类的时候,类中的数据可以被子类和其他包的类访问。
三、portected关键字
protected修饰类的时候,只有本包内的该类的子类和其他类可以访问该类中的成员变量和成员方法。
综上所述:
public 和 protected修饰的类可以由子类进行访问,如果子类和父类不在同一个包下,那么只能使用public 修饰才能保证该类可以被子类进行访问。如果父类不希望通过继承产生的子类访问他的成员变量,则需要使用private修饰它的成员变量。
Getter 和 Setter 是面向对象编程中的两个术语,它们分别代表访问器(Accessor)和修改器(Mutator)方法,是封装概念的一部分。这些方法用于访问和修改对象的私有属性。private
getter访问器和setter修改器是为private修饰的成员变量提供安全访问的一种方式。
Getter 方法
- 目的:提供一个公共方法来获取对象的私有属性值。
- 命名约定:通常以
get
开头,后跟属性名,属性名的首字母大写(如果属性名是多于一个单词,则每个单词的首字母大写,例如getFirstName
())。 - 返回值:返回私有属性的值。
Setter 方法
- 目的:提供一个公共方法来设置或修改对象的私有属性值。
- 命名约定:通常以
set
开头,后跟属性名,属性名的首字母大写。setModel(String model) - 参数:接受一个与要设置的属性类型相同的参数。
- 作用:可以在设置属性值之前执行验证或其他逻辑。
public class Car {
private String model; // 私有属性
private int year; // 私有属性
// 构造器
public Car(String model, int year) {
this.model = model;
this.year = year;
}
// Getter 方法
public String getModel() {
return model;
}
// Setter 方法
public void setModel(String model) {
this.model = model;
}
// Getter 方法
public int getYear() {
return year;
}
// Setter 方法
public void setYear(int year) {
if (year > 1886) { // 汽车是在1886年之后发明的
this.year = year;
} else {
System.out.println("Year must be after 1886.");
}
}
}
// 使用Getter和Setter
public class Main {
public static void main(String[] args) {
Car myCar = new Car("Toyota", 2020);
System.out.println("Car Model: " + myCar.getModel()); // 使用Getter获取模型
myCar.setModel("Honda"); // 使用Setter设置新的模型
myCar.setYear(1850); // 尝试设置一个不合理的年份,将被验证逻辑阻止
}
}
在这个示例中,Car
类有两个私有属性:model
和 year
。通过公共的 getModel()
和 setModel(String model)
方法可以访问和修改 model
属性,通过 getYear()
和 setYear(int year)
方法可以访问和修改 year
属性。setYear
方法中包含了一个简单的验证逻辑,确保年份不会早于汽车发明的时间。
Getter 和 Setter 是实现封装的一种方式,它们允许开发者控制对对象属性的访问和修改,同时隐藏了对象的内部实现细节。
定义
this关键字用于表示本类当前的对象,这个当前对象不是某个new出来的实体对象,而是当前正在编辑的类。this关键字只能在本类中使用。
this关键字 this
关键字是一个引用,它指向当前对象的实例
1、变量的访问原则是:就近原则
2、(1)作用:表示当前类型当前对象的引用
(2)哪个对象来调用this关键字所在的方法,this就指代哪个对象
(3)在set方法中,this该关键字在见名知意的前提下,用于区分哪个是对象的成员变量,剩下的
一个按照就近原则,就是局部变量。所以在set方法中,使用【this.成员变量的变量名】,一定是
成员变量, 没有使用name的变量,就按照就近原则去寻找
this
关键字是Java中一个非常有用的工具,它可以提高代码的清晰度和表达能力,尤其是在处理构造器链和同名变量时。
this的五种用法
1.引用当前实例的属性:当类的实例变量和局部变量或参数同名是,可用this区分它们。
public class Person {
private String name;
public Person(String name) {
this.name = name; // 使用 this 来引用实例变量
}
}
2.调用成员方法:
const person = {
firstName: 'John',
lastName: 'Doe',
fullName: function() {
return this.firstName + ' ' + this.lastName;
}
};
console.log(person.fullName()); // 输出: John Doe
3.在构造器中调用另一个构造器:在Java中,可以使用 this()
来调用同一个类中的另一个构造器,这称为构造器链。
public class Person{
private String name;
private int age;
public Person(){
this("Unknown",0);//调用带两个参数的构造器
}
public Person(String name,int age){
this.naem=name;
this.age=age;
}
}
4.返回当前实例:在方法中返回this可以允许链式调用(也称流式接口)。
public class Person{
Private String name;
public Person setName(String name){
this.name=name;
return this;//返回当前实例,允许链式调用
}
}
//使用链式调用
new Person().setName("Alice");
静态:如果所有对象,都具有一个共同的属性值,那么在这个属性上加上一个static,就会让该变量从原本的对象空间内,改为存储在【方法区的静态区】,从每一个对象都有一份变为所有对象共享一份.
一、静态变量
1、static,关键字;含义:静态;被他修饰的部分就是静态的
2、静态:不会随着对象的变化而变化
3、加载时机:随着类的加载而加载
4、静态优先于对象存在
5、静态变量被所有当前类型的对象共享
6、代码层面
(1)可以通过对象访问,格式:对象名.静态变量名
(2)可以通过静态访问,格式:类名.静态变量名
(3)静态变量可以通过对象或者类名的方式访问,在不创建对象的时候,也可以进行访问
二、静态方法
静态方法是使用关键字 static
修饰的方法。与静态变量类似,静态方法也属于类本身,而不是类的某个特定实例。这意味着可以在不创建类实例的情况下调用静态方法。
- 类级别访问:静态方法可以被类名直接调用,不需要实例化对象。
- 内存分配:静态方法存储在方法区,与静态变量类似。
- 访问静态成员:静态方法可以访问类的其他静态成员,包括静态变量和静态方法,但不能直接访问非静态成员。
- 构造函数调用:静态方法不能调用非静态构造函数,因为静态方法在类加载时就已经存在,而实例化对象是在运行时进行的。
- 继承:静态方法可以被子类继承,但子类不能重写(Override)父类的静态方法,只能隐藏(Hide)它。
- 使用场景:静态方法通常用于工具类或实用程序类中,它们提供一些与类实例状态无关的功能。
- 线程安全:如果静态方法修改了共享的静态变量,需要确保线程安全。
- 实例化方法的限制:静态方法不能直接访问类的非静态成员,如果需要访问,可以通过传递类实例作为参数的方式。
public class MyClass {
// 静态变量
public static int staticCounter = 0;
// 构造函数
public MyClass() {
// 每次创建实例时增加静态计数器
staticCounter++;
}
// 静态方法
public static void printCounter() {
System.out.println("Static counter: " + staticCounter);
}
}
public class Main {
public static void main(String[] args) {
// 直接通过类名访问静态变量
MyClass.staticCounter = 1;
// 创建两个MyClass的实例
MyClass obj1 = new MyClass();
MyClass obj2 = new MyClass();
// 打印当前静态计数器的值
MyClass.printCounter(); // 输出 "Static counter: 3"
}
}
三、静态代码块
"静态代码块"(Static Initializer Block)是一种特殊的代码块,它使用关键字 static
进行声明。静态代码块在类加载时执行,且只执行一次。它主要用于初始化静态变量,或者执行一些只需要执行一次的初始化操作。
以下是关于Java中静态代码块的一些要点:
- 执行时机:静态代码块在类加载时执行,也就是在创建类的任何对象之前,或者在直接调用类的静态方法之前。
- 执行顺序:静态代码块的执行顺序是按照它们在类中出现的顺序。
- 初始化静态变量:静态代码块通常用于初始化静态变量,尤其是当静态变量的初始化需要多步骤或者依赖于条件逻辑时。
- 与构造函数的区别:静态代码块在类加载时执行,而构造函数在对象实例化时执行。因此,静态代码块不能访问类的实例变量或调用非静态方法。
- 多静态代码块:一个类可以有多个静态代码块,它们将按照在类中出现的顺序依次执行。
- 线程安全:如果静态代码块中包含对静态变量的修改,需要确保线程安全,因为类加载可能在多线程环境中发生。
public class MyClass {
static int staticVar;
// 静态代码块
static {
// 初始化静态变量
staticVar = 100;
System.out.println("Static block executed, staticVar initialized to " + staticVar);
}
public MyClass() {
System.out.println("Constructor executed, staticVar is " + staticVar);
}
public static void main(String[] args) {
// 调用main方法时,静态代码块已经执行
MyClass obj = new MyClass();
}
}
第三方类库,在Java中,JAR(Java Archive)包是一种打包Java类文件和相关资源文件(如文本、图片等)的压缩包格式。JAR文件通常用于分发Java应用程序或库。JAR包是Java生态中非常重要的组成部分,它极大地方便了代码的组织、分发和重用
在Java中,JAR(Java Archive)包是一种打包Java类文件和相关资源文件(如文本、图片等)的压缩包格式。JAR文件通常用于分发Java应用程序或库。以下是关于JAR包的一些要点:
- 打包:JAR文件是一个ZIP格式的压缩文件,可以使用任何支持ZIP格式的工具来创建和解压。
- 分发:开发者可以将应用程序的所有组件打包成一个JAR文件,便于分发和部署。
- 重用:库开发者可以创建包含类和资源的JAR文件,供其他开发者作为依赖项重用。
- 封装:JAR文件可以包含Java类的私有资源和配置文件,有助于封装和隐藏实现细节。
- 压缩:由于JAR文件是压缩的,它们通常比将所有文件单独分发更节省空间。
- 执行:可以使用
java -jar
命令直接运行包含有清单文件(MANIFEST.MF)中指定主类的JAR文件。 - 库依赖:在构建Java项目时,可以指定其他JAR文件作为依赖项,构建工具(如Maven或Gradle)会自动处理这些依赖。
- 类加载器:Java虚拟机(JVM)使用类加载器机制来从JAR包中加载类。
- 清单文件:JAR文件可以包含一个清单文件(MANIFEST.MF),其中可以指定应用程序的属性,如主类名称、打包时间等。
- 签名:JAR文件可以被数字签名,以确保代码的完整性和来源。
# 创建不包含清单文件的JAR包
jar cvf myapp.jar com/myapp
# 创建包含清单文件的JAR包,并指定主类
echo "Main-Class: com.myapp.Main" > manifest.txt
jar cvfm myapp.jar manifest.txt com/myapp
在上述命令中,c 表示创建新的归档文件,v 表示在打包过程中显示详细输出,f 指定归档文件的名称,m 表示指定清单文件。
# 运行包含主类的JAR文件
java -jar myapp.jar