Java的导包,封装,构造器,代码块,类权限修饰符,static
包
就是文件夹,是管理 类 和 接口 的一种工具
作用:
- 方便管理类;
- 解决命名冲突(同一个路径下不能存在同名文件)的问题;
- 更好的保护类和类中的成员。
格式:
package 父包名[.子类名.子类名....]
。多级包用.分开即可包名规范
- 都小写
- 包名唯一
- com/org.公司名.部门名.项目名.模块名
- package 声明必须放第一位 ,后面是导包, 类
注意事项
- package语句必须是程序的第一条可执行的代码
- package语句在一个java文件中只能有一个
- 如果没有package,默认表示无包名
import关键字
导包的概述
- 不导包使用完全限定命名的方式:
包名.类 对象名 = new 包名.类名;
- 不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能
导包
Java中
java.lang包
的内容是自动导入的,不需要手动导入,其它必须手动导入。格式:
import 包名;
多个包下的类可以使用通配符: 使用
*
;- 想要导入 b.c.Test 不能使用
b.*
,b.*
只能导入b包下的类 - 如果二个包下类名相同, 使用完全限定命名的方式
- 想要导入 b.c.Test 不能使用
静态导包
java包的静态导入,用
import static
代替import
,不能说static import
。静态导入包是JDK1.5中的新特性。
静态导入包的注意事项:
- 如果静态导入的成员与本类的成员存在同名的情况下
- 那么默认使用本类的静态成员,
- 如果需要指定使用静态导入的成员,那么需要在静态成员前面加上类名
- Integer和Long都有一个MAX_VALUE常量,并且Java不会知道你在引用哪个MAX_VALUE。
- 如果静态导入的成员与本类的成员存在同名的情况下
在静态导入之前:
1
2
3
4
5
6
7public class TestStatic {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.toHexString(42));
}
}在静态导入之后:
1
2
3
4
5
6
7
8
9
10
11
12
13
14import static java.lang.System.out;
import static java.lang.Integer.*;
public class TestStaticImport {
public static void main(String[] args) {
// 我们不必在System.out.println中键入System
// 不必在Integer.MAX_VALUE中键入Integer
out.println(MAX_VALUE);
out.println(toHexString(42));
}
}
// 两个类都产生相同的输出:
2147483647
2a
封装
- 隐藏类中的属性和实现细节,对外只提供公共的访问方式
- 好处
- 访问的安全性
- 方法中可以使用流程控制语句
- 只能用我们提供的公共的访问方式访问
实现Java封装的步骤
- 采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
修改属性的可见性来限制对属性的访问(一般限制为private),例如:
1
2
3
4
5public class Person {
// 将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
private String name;
private int age;
}对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class Person{
private String name;
private int age;
//get,set访问器:命名习惯: getXxx();setXxx();
public int getAge(){
return age;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public void setName(String name){
this.name = name;
}
}
修改Set访问器
代码如下:
1
2
3
4
5
6
7
8public Demo setNama(String name){
this.name=name;
return this;
}
----------------
// 解决了构造方法参数记忆问题
Demo d = new Demo().setName("豆沙").setAge(18);
构造函数
方法名与类名相同;没有返回值类型,连void都没有
作用 :对象中的成员初始化
格式:
1
2
3访问修饰符 构造方法名(参数列表 ){
//初始化代码
}构造方法注意事项
- 如果我们没有给出构造方法,系统将自动提供一个无参构造方法。
- 如果我们给出了构造方法,系统将不再提供默认的无参构造方法。
- 注意:这个时候,如果我们还想使用无参构造方法,就必须自己给出。建议永远自己给出无参构造方法
构造方法加载机制
- 当通过调用子类的构造方法来实例化一个子类时,该构造方法会首先调用直接父类的无参数的构造方法。而父类的构造方法也会调用其直接父类的构造方法,这个过程不断重复,直到到达了java.lang.Object类的构造方法,即子类的所有父类也会实例化。
构造器的分类
默认构造器
- 我们类中没有显示定义构造器
- 那么,编译器会自动创建一个默认构造器
- 此方法无参,访问权限与类相同
自己定义: 显示定义了构造器 默认构造器就不存在
- 构造器可以重载
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34class Student5 {
int id;
String name;
int age;
// 默认构造器
// 默认构造函数分别为字段:id 和 name 分别提供了0和null值。
Student5(){
System.out.println(id + " " + name);
}
Student5(int i, String n) {
id = i;
name = n;
}
//通过参数的不同告知编译器此处用到了构造函数的重载
Student5(int i, String n, int a) {
id = i;
name = n;
age = a;
}
void display() {
System.out.println(id + " " + name + " " + age);
}
public static void main(String args[]) {
Student5 s1 = new Student5(111, "Karan");
Student5 s2 = new Student5(222, "Aryan", 25);
s1.display();
s2.display();
}
}
调用构造器
当我们需要使用多个构造器的时候,为了减少代码量,可以调用构造器
this 和super在构造函数中只能有一个,且都必须是构造函数当中的第一行。
- 使用this来调用自身的构造器
- super关键字,子类可以通过它调用父类的构造函数。
当父类的构造函数是无参构造函数时,在子类的构造函数中,就算不写super()去调用父类的构造函数,编译器会默认的去调用父类的无参构造函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class hood {
public hood(){
System.out.println("1");
}
}
class hood2 extends hood{
public hood2(){
System.out.println("2");
}
}
public class TEST {
public static void main(String[] args){
hood2 t = new hood2();
}
}
运行结果是:
1
2当父类的构造函数是有参构造函数时,此时如果子类的构造函数中不写super()进行调用父类的构造函数,编译器会报错,此时不能省去super()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class hood {
// 父类显示定义了构造器 默认构造器就不存在
public hood(int i){
System.out.println("1"+i);
}
}
class hood2 extends hood{
public hood2(){
// super()的无参构造不存在
super(5);
System.out.println("2");
}
}
public class TEST {
public static void main(String[] args){
// 结果: 15 2
hood2 t = new hood2();
}
}this关键字,可以理解为它可以调用自己的其他构造函数,看如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32class Father {
int age; // 年龄
int hight; // 身体高度
public Father() {
print();
this.age=20; //这里初始化 age 的值
}
public Father(int age) {
this(); // 调用自己的第一个构造函数
this.age = age; //这里修改 age 的值
print();
}
public Father(int age, int hight) {
this(age); // 调用自己第二个构造函数 ,下面的两个语句不执行的
this.hight = hight; //这里修改 hight 的值
print();
}
public void print() {
System.out.println("I'am a " + age + "岁 " + hight + "尺高 tiger!");
}
public static void main(String[] args) {
new Father(22,7);
}
}
I'am a 0岁 0尺高 tiger!
I'am a 22岁 0尺高 tiger!
I'am a 22岁 7尺高 tiger!
构造器和普通方法的区别
- 构造器就是用来初始化对象的
- 普通方法是为了完成特定的功能
- 构造只有 new 创建对象的时候才能调用;
- 普通方法 创建对象可随时调用
代码块
- 代码块概述
- 在Java中,使用{}括起来的代码被称为代码块。
- 代码块分类
- 根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块。
- 常见代码块的应用
- 局部代码块
- 在方法中出现;限定变量生命周期,及早释放,提高内存利用率
- 构造代码块
- 在类中方法外出现;多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行
- 静态代码块
- 在类中方法外出现,加了static修饰
- 在类中方法外出现,并加上static修饰;用于给类进行初始化,在加载的时候就执行,并且只执行一次。
- 局部代码块
局部块(成员代码块)
方法中用{}扩起来
代码块中的变量作用域在{}中
格式:
1
2
3
4
5
6方法(){
int n ;//局部
{
局部块:控制局部变量的声明周期 和 使用范围。
}
}
构造代码块
执行顺序: 构造代码块的执行次序优先于类构造函数。
构造代码块(非静态初始化块):
- 作用:给对象进行初始化。对象一建立就运行,且优先于构造函数的运行。
- 与构造函数的区别:非静态初始化块给所有对象进行统一初始化,构造函数只给对应对象初始化。
- 应用:将所有构造函数共性的东西定义在构造代码块中。
格式
1
2
3
4
5类{
{
构造块 : 初始化对象,new 执行, 优先于构造方法
}
}
静态代码块
作用:给类进行初始化。随着类的加载而执行,且只执行一次
与构造代码块的区别:
- 构造代码块用于初始化对象,每创建一个对象就会被执行一次;
- 静态代码块用于初始化类,随着类的加载而执行,不管创建几个对象,都只执行一次。
- 静态代码块优先于构造代码块的执行
- 都定义在类中,一个带static关键字,一个不带static
格式
1
2
3
4
5类{
static{
静态块: 作用 初始化类
}
}
小结
构造函数、非静态初始化块、静态代码块都是用于初始化,
三者的执行顺序依次是:静态代码块>构造代码块>构造函数。
其实构造代码块就是构造器的补充,初始化块是不能接收任何参数的,定义的一些所有对象共有的属性、方法等内容时就可以用初始化块初始化了。
静态初始化块的作用就是当JVM在装载类时,你想让它做一些事情,那么,就可以用静态初始化块。
这几者的执行顺序是:
- JVM在装载类时:先装载类的静态成员。加载 主类,静态代码块, main方法
- 加载 使用的类 到 方法区
- 加载 使用的类 的 静态成员 到方法区的静态区,默认初始化
- 加载 使用的类 的 静态成员和静态代码块,声明处初始化静态成员,静态代码块.
- 静态代码块只执行一次
- 同样,当一个类有继承自某类时,则会先装载该父类,那么,父类的装载或执行顺序,也都如上所述。
- 在创建类的实例时:先执行实例初始化块,再执行构造方法;但对于一棵继承树中,会先调用父类的构造方法。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238package pers.fulsun.cleanpicfxml;
class StaticCode {
int num = 9;
StaticCode() {
System.out.println("b");
}
static {
System.out.println("a");
}
{
System.out.println("c");
}
StaticCode(int x) {
System.out.println("d");
}
public static void show() {
System.out.println("show run");
}
}
class StaticCodeDemo {
static {
System.out.println("b");
}
public static void main(String[] args) {
new StaticCode(4);
}
}
// 结果
b
a
c
d
## 成员变量初始化顺序
1. 默认初始化:给默认值
2. 声明处初始化:
3. 构造代码块初始化
4. 构造器初始化
5. **注意:2,3 处看代码中定义的顺序**
- 代码说明构造器和声明的顺序
```java
public class Test {
//构造代码块初始化
{
System.out.println("默认初始化"+this.age);
age = 2;
System.out.println("默认初始化赋值: "+this.age);
}
// 声明处定义初始化
private int age=10;
{
System.out.println("声明处初始化: "+this.age);
}
Test(){
this.age= 1;
System.out.println("构造器初始化"+this.age);
}
public static void main(String[] args) {
Test test = new Test();
System.out.println("最后结果"+test.age);
}
}
默认初始化0
默认初始化赋值: 2
声明处初始化: 10
构造器初始化1
最后结果1
## 访问控制符
- Java有四种访问控制级别:public、protected、private和default(即不加修饰符,默认访问级别)
### 类访问控制符
- 类访问控制修饰符包括:public或默认访问级别
- 使用public访问控制修饰符使得类变为公有的,没有使用访问控制修饰符的类则具有默认的访问级别
- 公有类在任何地方都是可见的
- 默认访问级别的类只能由属于同一个包中的类使用
### 类成员访问控制符
类成员(方法、字段、构造方法等)可以具备四种访问控制级别之一
- public使得类成员成为公有的
- protected使得类成员成为受保护的
- private使得类成员成为私有的
- 没有使用访问控制修饰符的话,类成员将会拥有默认的访问级别
- 具体如下:
| 访问权限 | 本类 | 同包 | 子类 | 不同包 |
| --------- | ---- | ---- | ---- | ------ |
| private | 可以 | | | |
| 默认 | 可以 | 可以 | | |
| protected | 可以 | 可以 | 可以 | |
| public | 可以 | 可以 | 可以 | 可以 |
- protected修饰的话,若是子类,直接调用则可以,声明对象后再调用则不可以
## 类及其组成所使用的常见修饰符
- **A:修饰符:**
- 权限修饰符:private,默认的,protected,public
- 状态修饰符:static,final
- 抽象修饰符:abstract
- **B:类:**
- 权限修饰符:默认修饰符,public
- 状态修饰符:final
- 抽象修饰符:abstract
- 用的最多的就是:public
- **C:成员变量:**
- 权限修饰符:private,默认的,protected,public
- 状态修饰符:static,final
- 用的最多的就是:private
- **D:构造方法:**
- 权限修饰符:private,默认的,protected,public
- 用的最多的就是:public
- **E:成员方法:**
- 权限修饰符:private,默认的,protected,public
- 状态修饰符:static,final
- 抽象修饰符:abstract
- 用的最多的就是:public
- **F:除此以外的组合规则:**
- 成员变量: public static final
- 成员方法: public static / public abstract / public final
## static修饰符
### 作用和特点
- 可以用来修饰:成员变量,成员方法,代码块,内部类等。具体如下所示
- **修饰成员变量和成员方法**
- 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。
- 被static 声明的成员变量属于静态成员变量,静态变量存放在Java内存区域的方法区。
- **静态代码块**
- 静态代码块定义在类中方法外,静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)
- 该类不管创建多少对象,静态代码块只执行一次.
- **静态内部类(static修饰类的话只能修饰内部类)**
- 静态内部类与非静态内部类之间存在一个最大的区别:
- 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:1.它的创建是不需要依赖外围类的创建。2.它不能使用任何外围类的非static成员变量和方法。
- **静态导包(用来导入类中的静态资源,1.5之后的新特性):**
- 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以**直接使用类中静态成员变量和成员方法**。
- static关键字的特点
- 随着类的加载而加载
- 优先于对象存在
- 被类的所有对象共享
- 可以通过类名调用【静态修饰的内容一般我们称其为:与类相关的,类成员】
- **static的注意事项**
- 在静态方法中是没有this关键字的
- 静态是随着类的加载而加载,this是随着对象的创建而存在。
- 静态比对象先存在。
- 静态方法只能访问静态的成员变量和静态的成员方法【静态只能访问静态,非静态可以访问静态的也可以访问非静态的】
### static变量存储位置
- static变量存储位置
- 注意是:存储在JVM的方法区中
- static变量在类加载时被初始化,存储在JVM的方法区中,整个内存中只有一个static变量的拷贝,可以使用类名直接访问,也可以通过类的实例化对象访问,一般不推荐通过实例化对象访问,通俗的讲static变量属于类,不属于对象,任何实例化的对象访问的都是同一个static变量,任何地放都可以通过类名来访问static变量。
### 用static静态变量潜在性问题
- 用static静态变量潜在性问题
- 1.占用内存,并且内存一般不会释放;
- 2.在系统不够内存情况下会自动回收静态内存,这样就会引起访问全局静态错误。
- 3.在Android中不能将activity作为static静态对象,这样使activity的所有组件对象都存入全局内存中,并且不会被回收;
### 静态变量的生命周期
- 静态变量的生命周期
- 类在什么时候被加载?
- 当我们启动一个app的时候,系统会创建一个进程,此进程会加载一个Dalvik VM的实例,然后代码就运行在DVM之上,类的加载和卸载,垃圾回收等事情都由DVM负责。也就是说在进程启动的时候,类被加载,静态变量被分配内存。
### 静态变量何时销毁
- 静态变量何时销毁
- 类在什么时候被卸载?在进程结束的时候。
- 说明:一般情况下,所有的类都是默认的ClassLoader加载的,只要ClassLoader存在,类就不会被卸载,而默认的ClassLoader生命周期是与进程一致的
- 静态引用的对象回收
- 只要静态变量没有被销毁也没有置null,其对象一直被保持引用,也即引用计数不可能是0,因此不会被垃圾回收。因此,单例对象在运行时不会被回收
### 静态变量和成员变量的区别
- A:所属不同
- 静态变量属于类,所以也称为类变量
- 成员变量属于对象,所以也称为实例变量(对象变量)
- B:内存中位置不同
- 静态变量存储于方法区的静态区
- 成员变量存储于堆内存
- C:内存出现时间不同
- 静态变量随着类的加载而加载,随着类的消失而消失
- 成员变量随着对象的创建而存在,随着对象的消失而消失
- D:调用不同
- 静态变量可以通过类名调用,也可以通过对象调用
- 成员变量只能通过对象名调用
## 静态方法变量加载
- 描述Dog对象:
```java
public class Dog {
public static String name;
public static int age;
public static void showNameAge() {
System.out.println("name:" + name + " age:" + age);
}
}main测试方法:
1
2
3
4
5
6
7
8
9
10
11
12
13public class Demo01 {
public static void main(String[] args) {
Dog.name = "阿白";
Dog.age = 98;
Dog.name = "李双";
Dog.age = 90;
Dog.showNameAge();
}
}
//执行结果:name:李双 age:90大概流程就是
- 执行 java Demo01 是给JVM发送指令,和JVM说:把这个 Demo01.class 去执行;
- JVM就去执行 Demo01.class 文件里面的字节码,首先第一步 是把 Demo01.class字节码加载进内存;
- 第二步把Demo01.class放入字节码存放区;
- 第三步把Demo01里面的静态数据(静态变量 与 静态方法)分配到 静态区;
- 第四步main方法进栈,如何进栈的,是把静态区里面的main方法拿到运行区(栈) 然后就进栈了;
- 第五步main方法执行的时候,就在此时 才把Dog.class加载进内存;
- 第六步把Dog.class放入字节码存放区;
- 第七步把Dog里面的静态数据(静态变量 与 静态方法)分配到 静态区;
- 第八步 在main方法中执行 Dog.name 是向静态区去找到 Dog.name 拿来使用,由于是共享的 name 只保持最后修改的数据;