Java SE(7)——类和对象(二)
1.包(package)
1.1 包的定义
在Java中,包是一种用于组织和管理类,接口和其他包的机制。主要作用是防止命名冲突,并提供一种访问控制机制
1.2 package关键字
package关键字的主要作用是声明当前类在哪个包里面。
当然,用户也可以自定义包,在自定义的包中编写用户自己的代码。不过,自定义包的时候应该遵循以下规范
- 1.包名需要尽量指定成唯⼀的名字, 通常会用公司的域名的颠倒形式(比如:com.baidu.www)
- 2.包名使用小写,一般使用英文
- 3.如果一个java文件没有package语句,那么该java文件是被储存在一个默认包中(如:src)
- 包名应与java文件实际的存储路径一致
1.3 使用import导入类
在Java中,JDK已经提供了很多现成的类供我们使用,如果我们要使用就有两种方式
(1)使用类的全限定名:包名+类名(一般不推荐)
(2)先导入包,再使用类名(常见用法)
这段代码的作用是:将java.util包(文件夹)下面的Arrays类导入到当前的java文件中,当用户需要使用Arrays时,编译器就会根据该代码的路径去定位Arrays类,这就体现了包的访问控制机制
如果需要使用java.util包里的其他类,可以这么导入
import java.util.*;
星号的意思不是导入java.util包里的全部类,而是当我们需要使用java.util里的某类时,根据这个路径去定位目标类
注意:
Date类在javautil和java.sql包中都有,如果使用星号来导入类,就无法区分使用哪个包中的Date类,解决办法如下:
- 1.要么导入import java.util.Date,要么导入import java.sql.Date
- 2.使用类的全限定名
2.访问限定修饰符(Access Modifiers)
Java中的访问限定修饰符一共有四个:public,protected,default和private。主要用于控制类、方法和变量的可见性和访问权限
2.1 default
如果没有使用任何访问限定修饰符,就是default(包私有)访问权限
修饰类时:
修饰成员变量/方法时:
2.2 pubilc
被public声明的类/变量/方法都可以被任意的其他类访问,无论它们是否在同一包里
修饰类时:
修饰成员变量/方法时:
2.3 封装&private
2.3.1 封装的概念
封装是面相对象编程的三大基本特性之一(其他两个是继承和多态,后面的博文再介绍),指的是将类的数据(属性)和操作数据的方法绑定在一起,并对外隐藏内部实现的具体细节,仅提供必要的接口与外界交互。以电脑为例,电脑对外提供的接口就只是:开关机、通过键盘输入,显示器,USB插孔等,用户使用这些设备和电脑进行交互,完成日常事务
对于使用电脑的我们而言,只需要知道鼠标、键盘、开关机键等设备怎么使用就行了,至于为什么按了开机键电脑能亮起来,这个我们就不需要关心了。而要实现封装,就离不开private修饰符
2.3.2 private
使用private修饰的成员变量/方法只能在定义它们的类中访问,在外部类中一般是不能访问的(这里不考虑反射)
如果要访问使用private修饰的成员变量,可以对外提供一个get和set方法作为公开的接口,代码实现如下:
public class MyDate {private String year;private String month;private String day;/*//public MyDate() {this("2025","5","1");}public MyDate(String year, String month, String day) {this.year = year;this.month = month;this.day = day;}*///public String getYear() {return year;}public void setYear(String year) {this.year = year;}public String getMonth() {return month;}public void setMonth(String month) {this.month = month;}public String getDay() {return day;}public void setDay(String day) {this.day = day;}
}
class Test{public static void main(String[] args) {MyDate myDate = new MyDate();//使用get方法获取private修饰的成员变量System.out.println(myDate.getYear());System.out.println(myDate.getMonth());System.out.println(myDate.getDay());//使用set方法修改private修饰的成员变量myDate.setYear("2025");myDate.setMonth("5");myDate.setDay("1");//使用get方法获取private修饰的成员变量System.out.println(myDate.getYear());System.out.println(myDate.getMonth());System.out.println(myDate.getDay());}
}
运行结果如下:
- 1.降低代码的耦合性:封装可以降低对象和对象之间的耦合度,当对象内部发生变化时,只需要修改对象内部即可,不会影响到外部程序,因为公开的接口是不会发生改变的。例如,当上述MyDate类中的year变量修改为Year1时,对外提供的接口还是getYear和setYear,不会影响到外部代码
- 2.降低代码的复杂性:封装是隐藏了对象内部的实现细节,只提供了公开的接口给外部,使用起来更加简单
- 3.提高安全性:封装可以隐藏对象的具体实现细节,阻止外部程序直接访问对象的内部状态,从而保护数据被意外修改或破坏
注意:
- 1.类不能使用private修饰
- 2.访问限定修饰符不能修饰方法内部的局部变量,因为局部变量超出该方法范围就不能访问了
- 3.protected等到讲继承的时候在做介绍
3.static修饰符
static可以修饰成员变量/方法,至于static有什么用,这里先举个例子看看不适用static和使用static修饰的区别
3.1 static修饰成员变量
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所
有对象所共享的。当classroom不使用static修饰时,classroom变量存储在对象内部;当classroom使用static修饰时,classroom变量没有存储在对象内部。这是因为static成员属于类本身,而不属于某个对象,在整个内存中只存在一份副本,被所有对象共享,存储在方法区中。
以上面的代码为例,假如实例化的所有学生对象都属于同一个班的学生,当classroom不不使用static修饰时,每个对象内部都会保存一份classroom成员变量;当使用static修饰后,classroom变量不依附于对象而存在,存储在方法区中,被所有的学生对象共享。使用static修饰后的直接好处是,classroom变量在内存中只有一份,而不需要保存多份
静态/类变量特性:
- 1.不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 2.既可以通过对象的引用访问,也可以通过类名访问,但⼀般更推荐使用类名访问
- 3.静态/类变量存储在方法区中
- 4.生命周期和类一致(随类的加载而创建,随类的卸载而销毁)
3.2 static修饰成员方法
在Java中,使用static修饰的方法称为静态方法,是类的方法,不属于某个对象
public class Student {public String name;public int age;//静态成员变量public static String classroom;//构造方法public Student(String name, int age) {this.name = name;this.age = age;}//静态方法public static String getClassroom(){return classroom;}
}
静态方法的特性:
- 1.是类的方法,不属于某个对象
- 2.既可以通过对象的引用访问,也可以通过类名访问,但⼀般更推荐使用类名访问
- 3.静态方法内部不允许使用非静态的成员变量和方法
因为在调用非静态成员变量/方法时,会使用this引用,但是静态方法不属于某个对象,无法传递this引用。如果需要在静态方法中访问非静态成员变量/方法,只能通过对象来访问
public class Student {public String name;public int age;//静态成员变量public static String classroom;//构造方法public Student(String name, int age) {this.name = name;this.age = age;}//public void print(){System.out.println(name + " " + age);}//静态方法public static String getClassroom(){//实例化对象Student student = new Student("王五",20);return student.name;}
}
3.3 静态成员变量的初始化
静态成员变量一般不适用构造方法来初始化,因为构造方法是给某个对象来初始化变量,而静态变量是类的变量,被所有对象共享。所有静态成员变量的初始化分为:就地初始化和静态代码块初始化
3.3.1 就地初始化
在定义变量的时候指定初始值
public static String classroom = "100";
3.3.2 静态代码块初始化
那么接下来就要介绍什么是代码块了
4.代码块
使用{}
包围起来的代码就叫做代码块,根据代码块定义的位置和使用的关键字可分为四种
4.1 普通代码块
普通代码块:在方法中定义的代码块
public class demo1 {public static void main(String[] args) {{int a = 1;System.out.println("a = " + a);}int a = 2;System.out.println("a = " + a);}
}
运行结果:
a = 1
a = 2
4.2 构造/实例代码块
构造/实例代码块:在类中定义的代码块,一般用于初始化非静态成员变量
public class Student {public String name;public int age;//构造方法public Student(String name, int age) {this.name = name;this.age = age;}//public void print(){System.out.println(name + " " + age);}//构造/实例代码块{this.name = "八戒";this.age = 1000;}
}
class Test{public static void main(String[] args) {Student student1 = new Student("张三",18);student1.print();}
}
运行结果:
张三 18
根据运行结果可以判断出,实例代码块的执行时间比构造方法早(和定义的位置无关,因为上述代码中构造方法定义在前,但仍然是实例代码块先执行)
注意:
如果非静态成员变量有就地初始化操作,此时实例代码块和非静态成员变量谁先赋值就取决于它们定义的位置关系
4.3 静态代码块
public class Student {public String name;public int age;//静态成员变量public static String classroom;//构造方法public Student(String name, int age) {this.name = name;this.age = age;}//静态代码块static {classroom = "100";System.out.println("静态代码块执行");}
}
class Test{public static void main(String[] args) {Student student1 = new Student("张三",18);Student student2 = new Student("李四",19);System.out.println("classroom = " + Student.classroom);}
}
执行结果:
静态代码块执行
classroom = 100
注意:
- 1.不论实例化多少对象,静态代码块只会执行一次。上述代码实例化了两个对象,但是静态代码块只执行了一次
- 2.如果存在多个静态代码块,编译时会按照定义的先后顺序来执行
- 3.如果静态成员变量有就地初始化操作,此时静态代码块和静态成员变量谁先赋值就取决于它们定义的位置关系
public class Student {public String name;public int age;//静态代码块1static {classroom = "100";System.out.println("静态代码块1执行");}//静态代码块2static {classroom = "200";System.out.println("静态代码块2执行");}//静态成员变量public static String classroom = "300";//构造方法public Student(String name, int age) {this.name = name;this.age = age;}
}
class Test{public static void main(String[] args) {Student student1 = new Student("张三",18);Student student2 = new Student("李四",19);System.out.println("classroom = " + Student.classroom);}
}
执行结果:
静态代码块1执行
静态代码块2执行
classroom = 300
4.4 同步代码块
使用synchronized修饰的代码块,主要和多线程有关,这里不再详细讲解,我以前的博客中讲过