文章目录
1. 封装
1.1 封装的概念
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
举个例子,我们正在使用着的电脑,对于用户来说只需要通过键盘、鼠标、显示器就能成与计算机之间的交互,进而完成日常事务。
但实际上:电脑真正在工作的却是CPU、显卡、内存等一些硬件元件。
对于计算机使用者而言,不用关心内部有什么核心部件,只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
对于程序而言也可进行这样的操作,如:通过private
限定符修饰成员变量,然后通过特定的方法来调用这个私有变量
1.2 访问限定符
Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认识,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:
- public:任何情况下都可被访问
- protected:不同包中的非子类无法访问,其它情况都可以
- default:什么都不写时的默认权限,只能在同一个
包
里被访问 - private:只能在同一个
包
中的同一个类被访问(什么是包,往下翻
) - 访问权限除了可以限定类中成员的可见性,也可以控制类的可见性
代码示例:
public class Computer {
private String cpu; //CPU
private String memory; //内存
public String screen; //屏幕
String brand; //品牌 没有加限定符默认为default
public Computer(String cpu, String memory, String screen, String brand) {
this.cpu = cpu;
this.memory = memory;
this.screen = screen;
this.brand = brand;
}
public void Boot() {
System.out.println("开机");
}
public void PowerOff() {
System.out.println("关机");
}
public void SurfInternet() {
System.out.println("上网");
}
}
class TestComputer {
public static void main(String[] args) {
Computer computer = new Computer("HW","i7","25G","13*14");
System.out.println(computer.brand); //default属性:只能被本包中类访问
System.out.println(computer.screen);//public属性:可以被任何其他类访问
// System.out.println(computer.cpu); private属性:只能在Computer类中访问,不能被其他类访问
computer.Boot();
computer.PowerOff();
computer.SurfInternet();
}
}
注:一般情况下成员变量设置为private,成员方法设置为public
1.3 封装扩展之包
1.3.1 包的概念
为了更好的管理类,把多个类收集在一起称为一组,称为软件包
在Java中,包是对类、接口等封装机制的体现,是一种对类或者接口等很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
1.3.2 导入包中的类
Java中已经提供了很多现成的类供我们使用,如Date类:可以使用java.util.Date
导入java.util
这个包中的Date
类
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
//得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
但是这种写法比较麻烦,可以使用import语句导入包:
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date.getTime());
}
}
如果需要使用java.util
中的其他类,可以使用import java.util.*
,但更建议显示的指定要导入的类名,否则容易出现冲突
import java.util.*;
//import java.sql.*; 若加上这条语句。util和sql中都存在一个Date这样的类,此时就会出现歧义,编译出错
public class Test {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date.getTime());
}
}
可以使用import static导入包中静态的方法和字段:
import static java.lang.Math.*;
public class Test1 {
public static void main(String[] args) {
double x = 30.0;
double y = 40.0;
//double ret = Math.sqrt(Math.pow(x,2) + Math.pow(y,2)); 相比之下静态导入的方式更加方便
double ret = sqrt(pow(x,2) + pow(y,2));
System.out.println(ret);
}
}
1.3.3 自定义包
操作步骤:
-
在IDEA中新建一个包:右键src -> 新建 -> 包:
-
在弹出的对话框中输入包名:
-
-
在包中创建类,右键包名 -> 新建 -> 类,然后输入类名即可:
-
此时可以看到我们的磁盘上的目录结构已经被IDEA自动创建出来了
-
同时我们也能看到,在新创建的
Test1.java
文件的最上方就出现了一个package语句
1.3.4 包的访问权限控制举例
这里直接上代码示例,其中Computer类位于demo1包
中,TestComputer类位于demo2包
中:
package demo1;
public class Computer {
private String cpu; //CPU
private String memory; //内存
public String screen; //屏幕
String brand; //品牌
public Computer(String cpu, String memory, String screen, String brand) {
this.cpu = cpu;
this.memory = memory;
this.screen = screen;
this.brand = brand;
}
public void Boot() {
System.out.println("开机");
}
public void PowerOff() {
System.out.println("关机");
}
public void SurfInternet() {
System.out.println("上网");
}
}
package demo2;
import demo1.Computer;
public class TestComputer {
public static void main(String[] args) {
Computer computer = new Computer("HW","i7","25G","13*14");
System.out.println(computer.screen);//public属性:可以被任何其他类访问
System.out.println(computer.brand); //default属性:只能被本包中类访问,此处会报错
System.out.println(computer.cpu); //private属性:只能在Computer类中访问,不能被其他类访问,此处会报错
computer.Boot();
computer.PowerOff();
computer.SurfInternet();
}
}
注:
- public属性:可以被任何其他类访问
- default属性:只能被本包中类访问,此处会报错
- private属性:只能在Computer类中访问,不能被其他类访问,此处会报错
- 如果去掉Computer类之前的public修饰符,代码也会编译失败
1.3.5 常见的包
- java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入
- java.lang.reflect:java反射编程包
- java.net:网络编程开发包
- java.sql:进行数据库开发的支持包
- java.util:java提供的工具程序包
- java.io:I/O编程开发包
2. static成员
先引入一段代码:
public class Student {
private String name;
private int age;
private String gender;
private double score;
public Student(String name, int age, String gender, double score) {
this.name = name;
this.age = age;
this.gender = gender;
this.score = score;
}
public static void main(String[] args) {
Student s1 = new Student("Li lei",18,"男",3.8);
Student s2 = new Student("Han MeiMei",19,"女",4.0);
Student s3 = new Student("Jim",18,"男",2.6);
}
}
在这里定义了一个学生类,并进行了实例化
假设这三个同学是一个班的,且上课地点相同,那能否给类中再加一个成员变量来保存这个教室?可以,不过要给它加上static
来进行修饰,这样这间教室就能让所有学生来共享了,而不单是一个对象独享这间教室
2.1 static修饰成员变量
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
代码示例:
public class Student {
private String name;
private int age;
private String gender;
private double score;
public static String classroom = "J1707";
public Student(String name, int age, String gender, double score) {
this.name = name;
this.age = age;
this.gender = gender;
this.score = score;
}
public static void main(String[] args) {
Student s1 = new Student("Li lei",18,"男",3.8);
Student s2 = new Student("Han MeiMei",19,"女",4.0);
Student s3 = new Student("Jim",18,"男",2.6);
//静态成员变量可直接通过类名访问
System.out.println(Student.classroom);
//也可通过对象访问,单classroom是三个对象共享的
System.out.println(s1.classroom);
System.out.println(s2.classroom);
System.out.println(s3.classroom);
}
}
静态成员变量特性:
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 既可以通过对象访问,也可以通过类名访问,但更推荐使用类名访问
- 类变量存储在方法区中
- 生命周期伴随类的一生,随类的加载而创建,随类的卸载而销毁
- 被static修饰的变量不能是方法中的变量,为类变量
2.2 static修饰成员方法
一般类中的数据成员我们都设置为private,而成员方法设置为public,那设置之后,Student类中classroom属性如何在类外访问呢?
在类外调用会报错,那这个属性又该调用?
在Java中,被static修饰的成员方法称为静态成员方法,是类的方法,表示某个对象所特有的。而静态成员变量一般是通过静态方法来访问的
public class Student {
private String name;
private int age;
private String gender;
private double score;
private static String classroom = "J1707";
public Student(String name, int age, String gender, double score) {
this.name = name;
this.age = age;
this.gender = gender;
this.score = score;
}
public static String getClassroom() {
return classroom;
}
}
//注:这个是在src中重新定义的一个类
public class TestStudent {
public static void main(String[] args) {
System.out.println(Student.getClassroom());
}
}
//执行结果
J1707
静态方法特性:
- 不属于某个具体的对象,是类方法
- 可以通过对象调用,也可以通过**类名.静态方法名()**调用
- 不能在静态方法中访问任何非静态成员变量
public static String getClassRoom() {
this.age += 1; //不能在静态方法中访问任何非静态成员变量
return classRoom;
}
//编译失败 java: 无法从静态上下文中引用非静态 变量 age
4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时无法传递this引用
public static String getClassRoom() {
doClass();
return classRoom;
}
//编译报错 java: 无法从静态上下文中引用非静态 方法 doClass()
2.3 static成员变量初始化
静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化
-
就地初始化:定义时直接给初始值
public class Student { private String name; private int age; private String gender; private double score; private static String classroom = "J1707"; }
-
静态代码块初始化:那什么是代码块,往下看~
3. 代码块
3.1 代码块概念及分类
使用{}
定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:
- 普通代码块
- 构造块
- 静态块
- 同步代码块
3.2 普通代码块
定义在方法中的代码块(较少用)
代码示例:
public class Main {
public static void main(String[] args) {
{
int x = 10;
System.out.println("X1 = " + x);
}
int x = 100;
System.out.println("x2 = " + x);
}
}
//执行结果
X1 = 10
x2 = 100
3.3 构造代码块
定义在类中的代码块(不加修饰符),也叫实例代码块。一般用于初始化实例成员变量
代码示例:
package demo3;
public class Student {
private String name;
private int age;
private String gender;
private double score;
public Student() {
System.out.println("I am Studnet init()!");
}
//实例代码块(构造代码块)
{
this.name = "Mike";
this.age = 18;
this.gender = "man";
this.score = 95;
System.out.println("I am instance init()!");
}
public void show() {
System.out.println("name: " + name + " age: " + age + " gender " + gender + " score: " + score);
}
}
package demo3;
public class Main {
public static void main(String[] args) {
Student student = new Student();
student.show();
}
}
//执行结果
I am instance init()!
I am Studnet init()!
name: Mike age: 18 gender man score: 95.0
3.4 静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量
代码示例:
package demo3;
public class Student {
private String name;
private int age;
private String gender;
private double score;
public static String classRoom;
public Student() {
System.out.println("I am Studnet init()!");
}
//实例代码块(构造代码块)
{
this.name = "Mike";
this.age = 18;
this.gender = "man";
this.score = 95;
System.out.println("I am instance init()!");
}
//静态代码块
static {
classRoom = "J1707";
System.out.println("I am static init()!");
}
public void show() {
System.out.println("name: " + name + " age: " + age + " gender " + gender + " score: " + score);
}
}
/
package demo3;
public class Main {
public static void main(String[] args) {
Student s1 = new Student();
System.out.println("======================");
Student s2 = new Student();
}
}
//执行结果
I am static init()!
I am instance init()!
I am Studnet init()!
======================
I am instance init()!
I am Studnet init()!
从执行结果可以看出,代码块中存在着执行顺序:
- 静态代码块的先执行
- 然后执行构造代码块
- 再执行对应的构造方法
注:
- 静态代码块不管生成多少个对象,只执行一次
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
- 如果一个类中包含多个静态代码块,在编译时,编译器会按照定义的先后次序依次执行(合并)
- 构造代码块只有在创建对象时才会执行
4. 内部类
4.1 内部类的定义
**在Java中,可以将一个类定义在另一个类或者一个方法的内部,签字称为内部类,后者称未外部类。**内部类也是封装的一种体现。
public class OutClass {
class InnerClass {
}
}
//OutClass是外部类
//InnerClass是内部类
注:
- 定义在class类名{}花括号外部的,即使是在一个文件里,也不能称为内部类
public class A {
}
class B {
}
//A和B是两个独立的类,彼此之间没有关系
- 内部类和外部类共用一个java源文件,但是经过编译之后,内部类会形成独立的字节码文件
4.2 内部类的分类
根据内部类定义的位置不同,内部类一般可以分为以下几种形式:
- 成员内部类:
- 实例内部类:未被static修饰的成员内部类
- 静态内部类:被static修饰的成员内部类
- 局部内部类(几乎不会使用)、匿名内部类
代码示例:
public class OutClass {
//成员位置定义:未被static修饰-->实例内部类
public class InnerClass1 {
}
//成员位置定义:被static修饰 --> 静态内部类
static class InnerClass2 {
}
public void method() {
//方法中也可以定义内部类 -->局部内部类,几乎不用
class InnerClass3 {
}
}
}
4.2 实例内部类
在外部类中,内部类定义位置与外部类成员所处的位置相同,因此称为成员内部类
。而未被static修饰的成员内部类被称为实例内部类
package demo4;
public class OutClass {
private int a;
static int b;
int c;
public void methodA() {
a = 10;
System.out.println(a);
}
public static void methodB() {
System.out.println(b);
}
// 实例内部类:未被static修饰
class InnerClass {
int c;
public void methodInner() {
//在实例内部类中可以直接访问外部类中(任意访问限定符)修饰的成员
a = 100;
b = 200;
methodA();
methodB();
//如果外部类和实例内部类中具有相同名称成员时,优先访问的是内部类自己的
c = 300;
System.out.println(c);
//如果要访问外部类同名成员时,必须用 外部类名称.this.同名成员名字
OutClass.this.c = 400;
System.out.println(OutClass.this.c);
}
}
public static void main(String[] args) {
//外部类:对象创建以及成员访问
OutClass outClass = new OutClass();
System.out.println(outClass.a); //0
System.out.println(outClass.b); //0
System.out.println(outClass.c); //0
outClass.methodA(); //10
outClass.methodB(); //0
System.out.println("=============实例内部类的访问=============");
/*要访问实例内部类中成员,必须要创建实例内部类的对象*/
/*而实例内部类定义与外部内部类成员定义位置相同,因此创建实例内部类对象时必须借助外部类*/
//创建实例内部类对象
OutClass.InnerClass innerClass1 = new OutClass().new InnerClass();
//可以先将外部类对象创建出来,然后再创建实例内部类对象
OutClass.InnerClass innerClass2 = outClass.new InnerClass();
innerClass2.methodInner();
}
}
//执行结果
0
0
0
10
0
=============实例内部类的访问=============
10
200
300
400
注:
- 外部类中的任何成员都可以在实例内部类方法中直接访问
- 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
- 在实例内部类方法中访问同名的成员时,优先访问内部类中的成员,如果要访问外部类同名的成员,通过
外部类名称.this.同名名称
来访问 - 实例内部类对象必须在先有外部类对象的前提下才能创建
- 实例内部类的非静态方法中包含了一个指向外部类对象的引用
- 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象
4.3 静态内部类
被static修饰的内部成员类称为静态内部类
代码示例:
package demo4;
public class OutClass {
private int a;
static int b;
int c;
public static void methodB() {
System.out.println(b);
}
//静态内部类:被static修饰
static class InnerClass2 {
public void methodInner() {
//在该内部类中只能访问外部的静态成员
// a = 100; //编译失败,因为a不是静态成员
b = 200;
// methodA(); //编译失败,因为methodA()不是静态方法
methodB();
}
}
public static void main(String[] args) {
//静态内部类对象创建以及成员访问
OutClass.InnerClass2 innerClass21 = new OutClass.InnerClass2();
innerClass21.methodInner();
}
}
注:
- 在静态内部类中只能访问外部类中的静态成员
- 创建静态内部类对象时,不需要先创建外部类对象
5. 对象的打印
若用println()来打印对象:
package demo5;
public class Person {
String name;
String gender;
int age;
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public static void main(String[] args) {
Person person = new Person("Mike","男",18);
System.out.println(person);
}
}
//执行结果
demo5.Person@1b6d3586
执行结果并不是我们想要的对象的属性,这里我们可以重写toString方法(快捷键:Alt + Insert 选择toString())
package demo5;
public class Person {
String name;
String gender;
int age;
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) {
Person person = new Person("Mike","男",18);
System.out.println(person);
}
}
//执行结果
Person{name='Mike', gender='男', age=18}
这样就能打印出我们想要的对象属性了!