目录
一、什么是封装
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件。计算机厂商在电脑出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
二、访问限定符
Java 中主要通过 类和访问权限 来实现封装:
- 类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知;
- 而访问权限用来控制方法或者字段能否直接在类外使用。
Java中提供了四种访问限定符:
范围 | private | default | protected | public |
同一包中的同一类 | ||||
同一包中的不同类 | ||||
不同包中的子类 | ||||
不同包中的非子类 |
- public:可以理解为一个人的外貌特征,谁都可以看得到
- default: 什么都不写时的默认权限,对于自己家族中(同一个包中)不是什么秘密,对于其他人来说就是隐私了
- private:只有自己知道,其他人都不知道
- protected:因为涉及到子类,所以在继承部分详细介绍
//Computer.java
public class Computer {
private String cpu; //cpu
private String memory; //内存
public String screen; //屏幕
String brand; //品牌(没写限定符时默认为default)
public Computer(String brand,String cpu,String memory,String screen){
this.brand = brand;
this.cpu = cpu;
this.memory = memory;
this.screen = screen;
}
public void poweron(){
System.out.println("开机");
}
public void poweroff(){
System.out.println("关机");
}
}
//TestComputer.java
public class TestComputer {
public static void main(String[] args) {
Computer computer = new Computer("小米","R7","16","15.6");
System.out.println(computer.brand);
System.out.println(computer.screen);
//System.out.println(computer.cpu); //报错,只能在Computer类中访问
}
}
什么时候下用哪一种呢?
我们希望类要尽量做到 "封装",即隐藏内部实现细节, 只暴露出 必要 的信息给类的调用者。 因此我们在使用的时候应该尽可能的使用 比较严格 的访问权限。 例如如果一个方法能用 private, 就尽量不要用 public。
三、什么是包
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。
在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
1、导入包中的类
最熟悉的就是Scanner类,当你输入Scanner+回车,会自动出现;
import java.util.Scanner; //导入java.util这个包中Scanner类
public class Test{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
}
}
如果还想使用Java.util 这个包中的其他类,可以
import java.util.*;
但是我们更建议显式的指定要导入的类名. 否则还是容易出现冲突的情况,比如不同包中有同名的类,编译器编译时不知道你想使用的是哪个包中的类;如果你还是想用上面这种方法,那就需要完整的类名,以Date类举例
import java.util.*;
import java.sql.*; //这两个包中都有Date类
public class Test {
public static void main(String[] args) {
//util和sql中都存在Date类, 此时就会出现歧义, 编译出错
//Date date = new Date();
java.util.Date date = new java.util.Date(); //需要使用完整的类名
System.out.println(date.getTime()); //得到一个毫秒级别的时间戳
}
}
可以使用 import static 导入包中静态的方法和字段;
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
double x = 30;
double y = 40;
// 静态导入的方式写起来更方便一些.
// double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
double result = sqrt(pow(x, 2) + pow(y, 2));
System.out.println(result);
}
}
2、自定义包
(1)在 IDEA 中先新建一个包: 右键 src -> 新建 -> 包
(2)在弹出的对话框中输入包名, 例如 yxj
(3)在包中创建类 ,右键包名 -> 新建 -> 类,然后输入类名即可
(4)此时可以看到我们的磁盘上的目录结构已经被 IDEA 自动创建出来了
(5)同时我们也看到了, 在新创建的 Test.java 文件的最上方, 就出现了一个 package 语句
3、包的访问权限控制举例
以上面的Computer类和TestComputer类举例,将他们移入不同的包中
package demo1;
public class Computer {
private String cpu; //cpu
private String memory; //内存
public String screen; //屏幕
String brand; //品牌(没写限定符时默认为default)
public Computer(String brand,String cpu,String memory,String screen){
this.brand = brand;
this.cpu = cpu;
this.memory = memory;
this.screen = screen;
}
public void poweron(){
System.out.println("开机");
}
public void poweroff(){
System.out.println("关机");
}
}
package demo2;
import demo1.Computer;
public class TestComputer {
public static void main(String[] args) {
Computer computer = new Computer("小米","R7","16","15.6");
//System.out.println(computer.brand); //报错,brand是default,不允许被其他包中的类访问
System.out.println(computer.screen);
//System.out.println(computer.cpu); //报错,只能在Computer类中访问
}
}
四、static成员
在Java中,被 static 修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对 象,是所有对象所共享的。
1、static修饰成员变量
static 修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
我们创建一个学生类,假设三个同学是同一个班的,那么他们上课肯定是在同一个教室
//Student.java
public class Student {
public String name;
public String gender;
public int age;
public double score;
public static String classRoom = "高三一班"; //static 修饰成员变量
public Student(String name, String gender, int age, double score) {
this.name = name;
this.gender = gender;
this.age = age;
this.score = score;
}
}
//TestStudent.java
public class TestStudent {
public static void main(String[] args) {
// 静态成员变量可以直接通过类名访问
System.out.println(Student.classRoom); //通过类来访问
Student s1 = new Student("小张", "男", 18, 3.8);
Student s2 = new Student("小刘", "女", 19, 4.0);
Student s3 = new Student("小李", "男", 18, 2.6);
}
}
【静态成员变量特性】
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
- 类变量存储在方法区当中
- 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
2、static修饰成员方法
一般类中的数据成员都设置为private,而成员方法设置为public,那设置之后,Student类中classRoom属性如何在类外访问呢?
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。
//Student.java
public class Student {
public String name;
public String gender;
public int age;
public double score;
private static String classRoom = "高三一班"; //public 改为 private
//加这个静态成员方法
public static String getClassRoom() {
return classRoom; //返回静态成员变量
}
}
//TestStudent.java
public class TestStudent {
public static void main(String[] args) {
//报错
//System.out.println(Student.classRoom);
//通过访问静态方法来访问private修饰的静态成员变量
System.out.println(Student.getClassRoom());
}
}
【静态成员方法特性】
- 不属于某个具体的对象,是类方法
- 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者
- 不能在静态方法中访问任何非静态成员变量
- 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用
- 静态方法无法重写(后续多态讲解)
五、static成员变量初始化
静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。
1、就地初始化
在定义时直接给出初始值,上面的学生类的静态成员变量就是这么初始化的。
2、静态代码块初始化
在下面的代码块中讲解。
六、代码块
使用 {} 定义的一段代码称为代码块。
根据代码块定义的位置以及关键字,又可分为以下四种:
- 普通代码块
- 构造块
- 静态块
- 同步代码块(后续详解)
1、普通代码块
定义在方法中的代码块。
public class TestStudent {
public static void main(String[] args) {
//普通代码块
{
//...
}
}
}
2、构造代码块
定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
public class TestStudent {
//实例代码块
{
//...
}
public static void main(String[] args) {
//普通代码块
{
//...
}
}
}
3、静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
public class TestStudent {
//静态代码块
static{
//...
}
//实例代码块
{
//...
}
public static void main(String[] args) {
//普通代码块
{
//...
}
}
}
【注意】
- 静态代码块不管生成多少个对象,其只会执行一次
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
- 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
- 实例代码块只有在创建对象时才会执行
七、初始化的顺序
public class TestStudent {
//静态代码块
static{
System.out.println("静态代码块执行");
}
//实例代码块
{
System.out.println("实例代码块执行");
}
//构造方法
public TestStudent() {
System.out.println("构造方法执行");
}
public static void main(String[] args) {
TestStudent testStudent = new TestStudent();
}
}
//输出
//静态代码块执行
//实例代码块执行
//构造方法执行
完