匿名对象
什么是匿名(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() {
}
}
- 程序的打印结果应该什么呢?
package和import关键字
package和import两个关键字涉及到了一个Java文件中的包的操作
package
作用?
-
在Java源文件的第一行,使用package关键字声明
- 声明该Java源文件中,所定义的所有类,都属于同一个包
- 一个Java源文件,只能有一个public修饰的类
- package关键字后,跟上当前类的包名,表示所处的包
- 声明该Java源文件中,所定义的所有类,都属于同一个包
-
语法
-
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导入很少使用,了解认识即可
如果不同包下两个同名类,我都想使用咋办?
- 真的存在这种需求,建议改名一个
- 一个用全限定类名去使用