• 就是文件夹,是管理 类 和 接口 的一种工具

  • 作用:

    1. 方便管理类;
    2. 解决命名冲突(同一个路径下不能存在同名文件)的问题;
    3. 更好的保护类和类中的成员。
  • 格式:package 父包名[.子类名.子类名....]。多级包用.分开即可

  • 包名规范

    • 都小写
    • 包名唯一
    • com/org.公司名.部门名.项目名.模块名
    • package 声明必须放第一位 ,后面是导包, 类
  • 注意事项

    • package语句必须是程序的第一条可执行的代码
    • package语句在一个java文件中只能有一个
    • 如果没有package,默认表示无包名

import关键字

导包的概述

  • 不导包使用完全限定命名的方式: 包名.类 对象名 = new 包名.类名;
  • 不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能

导包

  • Java中java.lang包的内容是自动导入的,不需要手动导入,其它必须手动导入。

  • 格式:import 包名;

  • 多个包下的类可以使用通配符: 使用*;

    • 想要导入 b.c.Test 不能使用b.*,b.*只能导入b包下的类
    • 如果二个包下类名相同, 使用完全限定命名的方式

静态导包

  • java包的静态导入,用import static代替import,不能说static import

  • 静态导入包是JDK1.5中的新特性。

  • 静态导入包的注意事项:

    • 如果静态导入的成员与本类的成员存在同名的情况下
      • 那么默认使用本类的静态成员,
    • 如果需要指定使用静态导入的成员,那么需要在静态成员前面加上类名
    • Integer和Long都有一个MAX_VALUE常量,并且Java不会知道你在引用哪个MAX_VALUE。
  • 在静态导入之前:

    1
    2
    3
    4
    5
    6
    7
    public 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
    14
    import 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变量)之间发生的同名的冲突。
  1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:

    1
    2
    3
    4
    5
    public class Person {
    // 将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
    private String name;
    private int age;
    }
  2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public 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
    8
    public 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
    34
    class 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
    19
    class 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
    20
    class 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
    32
    class 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 220尺高 tiger!
    I'am a 22岁 7尺高 tiger!

构造器和普通方法的区别

  1. 构造器就是用来初始化对象的
  2. 普通方法是为了完成特定的功能
  3. 构造只有 new 创建对象的时候才能调用;
  4. 普通方法 创建对象可随时调用

代码块

  • 代码块概述
    • 在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在装载类时,你想让它做一些事情,那么,就可以用静态初始化块。

  • 这几者的执行顺序是:

    1. JVM在装载类时:先装载类的静态成员。加载 主类,静态代码块, main方法
    2. 加载 使用的类 到 方法区
    3. 加载 使用的类 的 静态成员 到方法区的静态区,默认初始化
    4. 加载 使用的类 的 静态成员和静态代码块,声明处初始化静态成员,静态代码块.
    5. 静态代码块只执行一次
    6. 同样,当一个类有继承自某类时,则会先装载该父类,那么,父类的装载或执行顺序,也都如上所述。
    7. 在创建类的实例时:先执行实例初始化块,再执行构造方法;但对于一棵继承树中,会先调用父类的构造方法。
  • 示例:

    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
    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. 构造器初始化
  • 注意: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
    30
    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对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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
    13
    public class Demo01 {
    public static void main(String[] args) {
    Dog.name = "阿白";
    Dog.age = 98;

    Dog.name = "李双";
    Dog.age = 90;

    Dog.showNameAge();
    }
    }

    //执行结果:name:李双 age:90
  • 大概流程就是

    1. 执行 java Demo01 是给JVM发送指令,和JVM说:把这个 Demo01.class 去执行;
    2. JVM就去执行 Demo01.class 文件里面的字节码,首先第一步 是把 Demo01.class字节码加载进内存;
    3. 第二步把Demo01.class放入字节码存放区;
    4. 第三步把Demo01里面的静态数据(静态变量 与 静态方法)分配到 静态区;
    5. 第四步main方法进栈,如何进栈的,是把静态区里面的main方法拿到运行区(栈) 然后就进栈了;
    6. 第五步main方法执行的时候,就在此时 才把Dog.class加载进内存;
    7. 第六步把Dog.class放入字节码存放区;
    8. 第七步把Dog里面的静态数据(静态变量 与 静态方法)分配到 静态区;
    9. 第八步 在main方法中执行 Dog.name 是向静态区去找到 Dog.name 拿来使用,由于是共享的 name 只保持最后修改的数据;