Java学习 day07_extends

匿名对象

什么是匿名(anonymous)对象?

  • 没有名字的对象就是匿名对象

  • 这个名字是引用类型变量名,也就是引用的名字

  • 语法:就是创建一个对象,没有引用指向它

    new 类名();
    
  • 从实质上看,就是没有被栈上的引用指向,只孤零零的存在于堆上的对象就是匿名对象

匿名对象有啥用?

  • a,可以用匿名对象作为方法的返回值
  • b,作为对象调用方法
  • c,可作为方法的实参传递给方法
// 几种用途演示
public class Demo {
    public static void main(String[] args) {
        //创建一个匿名对象
        //new Student().method();
        //new Student().method();
        //new Student().method();
        //new Student().method();
        //new Student().method();
        //new Student().method();

        test(new Student());
    }

    public static Student test() {
        /*Student s = new Student();
        return s;*/
        //上述步骤二合一
        return new Student();
    }

    public static void test(Student s){
        s.method();
    }
}

class Student {
    public void method() {
        System.out.println("Student");
    }
}


# 代码块(block)

代码块概念

  • 在Java中,使用大括号”{}“括起来的代码被称为代码块,根据其位置和声明方式的不同,可以分为:
    • 局部代码块
    • 构造代码块
    • 静态代码块
    • 同步代码块(后边再说)
  • 总体而言,代码块在实际开发中,使用频率并不高,可替代性也很强
    • 但是由于其迷惑性极强,常年出没于各种面试题中
    • 这个东西很绕,需要深入理解

局部代码块(local)

什么是局部代码块?

  • 和局部变量位置相似,处在方法中局部位置的代码块,称之为局部代码块
  • 声明方式:{}
  • 位置:方法中的局部位置
  • 代码执行顺序:从上到下
  • 作用:限定局部变量的作用范围和生命周期,及早释放,提高内存利用率
  • 实际用途:并没有多大用,不要在代码中使用局部代码块,早起Java程序员用来提高内存利用效率

构造代码块(building)

什么是构造代码块

  • 和构造方法类似,处在类中成员位置的代码块,称之为构造代码块
  • 声明:{}
  • 位置:在类中成员位置
  • 作用:构造代码块会随着构造器的执行而执行,不管是哪个构造器,也就是new对象的时候

总结一下到目前为止,我们给成员变量赋值的方式:
分情况讨论
1,创建对象的时候
- a,默认初始化
- b,显式赋值
- c,构造器
- d,构造代码块
2,创建对象之后
- a,对象名点去赋值
- b,成员方法给成员变量赋值

  • new对象过程,构造代码块的执行顺序
    • 对象创建出来后,就把所有成员变量的值默认初始化,然后开始其余赋值操作
    • 如果构造器在第一行显式的调用了另一个构造器,那么程序先跳转到this构造器,但并不会执行
    • 而是
      • 按照类中定义代码块和成员变量的位置,从上到下执行代码块和成员变量自身的初始化语句
    • 继而执行该this构造器代码
    • 最后执行该构造器代码: 构造器一定是最后执行的
  • 需要注意的是:
    • 应该永远将构造代码块,放在成员变量的定义语句下面
      • 一方面,如果代码块初始化在上,成员变量声明在下,逻辑上会很奇怪
      • 另一方面,如果对象的创建依赖于代码块和成员变量的定义位置,很容易引发错误
    • 可以使用this关键字,但是意义不大

显式赋值和构造代码块,谁在代码书写顺序的上面,谁就先执行,反之在下面的要后执行


编译器在编译完代码后,实际上并不存在构造代码块的结构
编译器会智能的把显式赋值和构造代码块的代码,放入所有构造器的第一行,这么做:
1,为了保证变量都是先声明,再赋值
2,为了保证按照代码的书写顺序,从上到下执行显式赋值和构造代码块
- 写在书写顺序上面的就变成了显式赋值
- 写在书写顺序下面就跑到了构造器里面去了
3,为了保证构造代码块随着构造器的执行而执行(如果构造代码块中存在不是赋值语句的语句,会直接放入构造器的第一行)

package com.cskaoyan.building;

public class Building {
    public static void main(String[] args) {
        C c = new C(88);
    }
}

class C{
    {
        int a = 100;
    }
    int a = 99;
    public C(int a){
        this.a = a;
    }
}
// 在编译后的代码中,a=99被放在了构造函数中
  • 实践用途:
    • 构造器只是创建某一个对象时调用的,但构造代码块却是创建该类每个对象都会调用的
    • 因此,可以抽取出所有构造器都需要做的事情,放入构造代码块中

静态代码块

什么是静态代码块

  • 使用static关键字修饰的构造代码块,处在类中的成员位置,称之为静态代码块
  • 声明:static{}
  • 位置:类中成员位置
  • 作用:和静态成员一样,随着类加载而执行,一般用于给静态成员变量赋值,只执行一次
  • new对象过程,静态代码块、构造代码块、构造方法的执行顺序
    • 静态代码块
    • 构造代码块
    • 构造方法
  • 需要注意的是:
    • 静态代码块和静态成员一样,不能在里面调用非静态
    • 除非是静态成员变量需要很复杂的初始化代码,否则没太大必要使用,直接显式赋值就行
    • 静态代码块和静态成员变量的定义顺序,也会影响到静态成员变量的最终取值(谁在下面,谁后执行)
      • 所以应该永远将静态代码块放在静态成员变量的定义下面
    • 构造代码块可以给静态成员变量赋值,静态代码块却不能给普通成员代码块赋值
      • 若构造代码块和静态代码块同时给一个静态成员变量赋值
        • 它们在代码中的顺序,并不会对结果造成影响
      • 因为静态代码块总是先于构造代码块执行

一些实际的使用案例

  • 构造代码块使用场景:
    • 不想让对象共享一个属性(static),但是还需要对象的某个属性具有相同初始值。
    • 比如公司新来了一批员工,他们的初始薪资一样,但是后面会根据表现调薪
      • 这个时候如果用static,显然不合适,因为调薪是针对个人的
      • 使用普通的成员变量定义薪资,然后用构造代码块给变量赋值
  • 静态代码块的经典使用场景:
    • 复杂的静态变量初始化
    • JDBC的驱动加载(Java EE学)

一定会触发类加载的几种情况

  • 执行某个类的main方法,一定会进行类加载
  • 创建某个类的对象,一定会进行类加载
  • 访问某个类的静态成员,一定会进行类加载

牛刀小试

查看以下代码,请回答程序运行的结果

public class ExerciseBlock {
// 2.静态代码块在类加载时执行,输出第一个
    static {
        System.out.println("main方法静态代码块!");
    }
    {
        System.out.println("main方法构造代码块!");
    }
    // 1.程序的入口,但是要先去进行所在类的类加载
    public static void main(String[] args) {
    // 3.回来执行main方法,输出main方法开始执行!
        System.out.println("main方法开始执行!");
 
        Star s = new Star(8,"马化腾");
	//6.按照顺序,Star.name是刘亦菲
        System.out.println(Star.name);
    // 7. s.age就是传递的参数,也就是8
        System.out.println(s.age);
    }
}
class Star{
    {
        age = 18;
        Star.name = "杨超越";
        System.out.println("我喜欢杨超越");
    }
    // 4.main方法创建了star对象,先处理static修饰的,static代码块输出我喜欢杨幂
    static String name = "王菲";
    int age = 28;
    static {
        name = "杨幂";
        System.out.println("我喜欢杨幂");
    }
// 5.创建对象使用这个构造器,在里面调用下面的构造器,不过还是先执行构造代码块里的东西
// 所以输出我喜欢杨超越,age和age,name
    public Star(int age,String name) {
        this(age);
        System.out.println("age,name:构造器!");
        Star.name = name;
        Star.name = "刘亦菲";
    }
    public Star(int age) {
        System.out.println("age:构造器!");
        this.age = age;
    }
    public Star() {
    }
}
  • 程序的打印结果应该什么呢?
    Java学习 day07_extends


package和import关键字

package和import两个关键字涉及到了一个Java文件中的包的操作

package

作用?

  • 在Java源文件的第一行,使用package关键字声明

    • 声明该Java源文件中,所定义的所有类,都属于同一个包
      • 一个Java源文件,只能有一个public修饰的类
    • package关键字后,跟上当前类的包名,表示所处的包
  • 语法

    • package 包名;
      
  • 注意

    • 包名的书写,用逗号隔开
    • 语法上,该声明语句一定位于一个Java源文件的有效代码的第一行,否则会报错
      • 注释不算有效代码,你可以把package声明放在注释下面
      • 但是根据规范,不要这么做
      • 应该将package永远放在Java源文件真正意义的第一行
      • 3,如果一个class直接放在src目录根目录,它就无需包声明

全限定类名

什么是全限定类名?

  • 可以唯一确定一个类的,由包名加上类名组成的字符串
  • 不做任何操作的情况下,直接输出一个类的对象,会打印全限定类名
  • 例如:com.cskaoyan.oop.statickeyword.Star

import

引例

  • 在包名为onepackage的包中创建一个Student类
  • 在包名为anotherpackage的包中创建一个同名Student类
  • 在onepackage包下,写一个测试类Test
  • 在测试类Test中,创建anotherpackage包中Student类的对象

显然编译器会默认优先搜索同包下的,Student类,然后去创建它的对象

为了让编译器,去获取到不同包下的,Student类,必须由程序员显式的告诉编译器怎么去找到这个类

有两种解决方法

  • 在创建类的对象的语句中,不使用简单的类名,而是使用全限定类名

    • 不可能真的这么做,因为全限定类名太长了
  • 使用import关键字,声明要使用的类

    • 语法

      • import 全限定类名;
        
    • 语法上import关键字应该放在,package声明语句和类声明语句之间

      • 和package关键字一样,import声明应该永远紧跟在package声明之后(规范)

      • import声明提供了一种包的智能导入方式,语法为

        • import <包名>.*;
          
        • 包中的类将根据需要导入,避免使用多条import声明

        • 需要注意的是,如果本包中已存在同名类,就判定为不需要导包,.*就不会自动导包,只有在不导包就会报错的情况下,才会使用到智能导包

    • Java语言核心包java.lang包中的类将被隐式导入,可以直接使用其中的类

      • import java.lang.*;
        
      • 我们使用的String、Integer、System都属于这个包

  • 静态导入

    • 语法

      • import static 全限定类名.*;
        import static 全限定类名.静态成员;
        
    • 可以用来导入静态方法和静态成员变量

    • 以往我们调用不同包的静态成员,通过类名.成员名访问,如果静态导入,可以省略类名

    • 例如

      • import static java.lang.System.*;
        
      • 可以这样写输出语句

        out.println("Hello World!");
        
      • import static java.lang.Math.*;
        
      • 可以这样使用

        double pi = PI;
        double result = pow(2,3);
        
  • 实践开发中,static导入很少使用,了解认识即可


如果不同包下两个同名类,我都想使用咋办?

  • 真的存在这种需求,建议改名一个
  • 一个用全限定类名去使用

上一篇:JavaSE_day07_类的成员之方法(method)


下一篇:零基础入门学习JAVA课堂笔记 ——DAY07