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
41class 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
成员变量初始化顺序
- 默认初始化:给默认值
- 声明处初始化:
- 构造代码块初始化
- 构造器初始化
注意:2,3 处看代码中定义的顺序
代码说明构造器和声明的顺序
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
30public 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是随着对象的创建而存在。
- 静态比对象先存在。
- 静态方法只能访问静态的成员变量和静态的成员方法【静态只能访问静态,非静态可以访问静态的也可以访问非静态的】
- 在静态方法中是没有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对象:
1
2
3
4
5
6
7
8
9public 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 只保持最后修改的数据;