代码块
基本介绍:代码化块又称为初始化块,属性类中的成员【即是类的一部分】,类似于方法,将逻辑语句封装在方法体中,通过{}
包围起来
但和方法不同,没有方法名,没有返回值,没有参数,只有方法体,而且不用通过对象或类显示调用,而是加载类时,或创建对象时隐式调用。
基本语法
[修饰符] {代码};
说明注意:
- 修饰符可选,要写的话,也只能写
static
- 代码块分为两类,使用
static
修饰的叫静态代码块,没有static
修饰的,叫普通代码块或非静态代码块 - 逻辑语句可以为任何逻辑语句(输入,输出,方法调用,循环,判断等)
;
号可以写上,也可以省略
代码块作用的结论:(记住)
- 可以把相同的语句,放入到一个代码块中
- 当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容
- 代码块调用的顺序优先于构造器
代码块的好处和案例演示:
- 相当于另外一个形式的构造器(对构造器的补充机制),可以做初始化的操作
- 场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性。
- 代码块的快速入门:
CodeBlock01.java
package JAVA面向对象高级部分.static_.codeBlock;
public class CodeBlock01 {
public static void main(String[] args) {
Movie movie = new Movie("寒战2");
System.out.println("--------------------");
Movie movie1 = new Movie("无双", 125.168, "无双导演");
}
}
class Movie{
private String name;
private double price;//时长
private String director;
/**
* 代码块的作用:
* 代码块是优先于构造器执行的
* 不管用那个构造器初始化,都会调用非静态代码块
*/
{
System.out.println("电影屏幕打开。。。");
System.out.println("电影广告。。。");
System.out.println("电影开始播放。。。");
}
public Movie() {
// System.out.println("电影屏幕打开。。。");
// System.out.println("电影广告。。。");
// System.out.println("电影开始播放。。。");
}
public Movie(String name) {
this.name = name;
System.out.println("Movie(String name) 构造器被调用。。。");
// System.out.println("电影屏幕打开。。。");
// System.out.println("电影广告。。。");
// System.out.println("电影开始播放。。。");
}
public Movie(String name, double price, String director) {
this.name = name;
this.price = price;
this.director = director;
System.out.println("Movie(String name, double price, String director) 被调用。。。");
// System.out.println("电影屏幕打开。。。");
// System.out.println("电影广告。。。");
// System.out.println("电影开始播放。。。");
}
}
返回值:
电影屏幕打开。。。
电影广告。。。
电影开始播放。。。
Movie(String name) 构造器被调用。。。
--------------------
电影屏幕打开。。。
电影广告。。。
电影开始播放。。。
Movie(String name, double price, String director) 被调用。。。
代码块的使用注意事项和细节说明(七点必须记住)
静态代码块是针对类的加载的,而普通代码块是针对创建对象初始化的(普通代码块是构造器的补充机制,可以认为是一个特殊的构造器)
1.static
代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象就执行
2.类什么时候被加载(三种情况)【记住】
- 创建对象实例时
(new)
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员(成员方法和成员属性)
3.普通的代码块,在创建对象实例时,会被隐式的调用,被创建一次,就会调用一次。如果只是使用类的静态成员时,普通代码块并不会执行[意思是如果只使用静态成员时,不会调用普通代码块]
小结:(1)static
代码块是类加载时,执行,只会执行一次
(2)普通代码块是在创建对象时调用的,创建一次,调用一次
(3)类加载3种情况,需要记住
注意:当两个类继承关系时,先执行父类的静态代码块,再执行子类的静态代码块,最后再执行父类的非静态代码块和子类的非静态代码块,非静态代码块是创建对象时才会被执行
package JAVA面向对象高级部分.static_.codeBlock;
public class CodeBlockDetail01 {
public static void main(String[] args) {
// A a = new A();
// A a1 = new A();
/**
* 返回值是:
我是静态代码块
我是普通代码块
我是普通代码块
*/
System.out.println("-----------------");
// C c = new C();
/**返回值:
* 我是B类的静态代码块
* 我是C类的静态代码块
* 我是B类的非静态代码块
* 我是C类的非静态代码块
*/
System.out.println("------------------");
//注意是:非静态代码块是创建对象时,才会被执行
//System.out.println(C.name);
/**
* 我是B类的静态代码块
* 我是C类的静态代码块
* 我是程序员!!!呵呵
*/
System.out.println("----------------");
System.out.println(D.number);
/**
* 返回值:
* 我是D类的静态代码块
* 168
*/
}
}
/**
* 第一个细节:
* static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类加载而执行
* 并且只会执行一次,如果是普通代码块,每创建一个对象就执行一次
*/
class A {
//静态代码块,注意代码块只能用static修饰,只会加载一次,随着类加载而加载
static {
System.out.println("我是静态代码块");
}
//非静态代码块(普通代码块),每创建一个对象就执行一次
{
System.out.println("我是普通代码块");
}
}
/**
* 类什么时候被加载【必须记住】
* 1.创建对象实例时(new)
* 2.创建子类对象实例,父类也会被加载
* 3.使用类的静态成员时(静态属性或静态方法)
*/
class B {
//静态代码块
static {
System.out.println("我是B类的静态代码块");
}
//非静态代码块(普通代码块)
{
System.out.println("我是B类的非静态代码块");
}
}
class C extends B {
public static String name = "我是程序员!!!呵呵";
//C类静态代码块
static {
System.out.println("我是C类的静态代码块");
}
//C类非静态代码块
{
System.out.println("我是C类的非静态代码块");
}
}
/**
* 普通的代码块,在创建对象实例时,会被隐式的调用,被创建一次,就会调用一次,
* 如果只是使用类的静态成员时,普通代码块是不会被执行的,普通代码块是针对创建对象的
*/
class D {
public static int number = 168;
//静态代码块,在加载类信息时,就会被调用一次,并且只会调用一次
static {
System.out.println("我是D类的静态代码块");
}
//非静态代码块,如果不创建对象时,是不会调用非静态代码块的,并且创建一次调用一次
{
System.out.println("我是D类的非静态代码块");
}
}
说明使用类的静态成员【成员方法和属性】时,都会使类的加载
代码块细节说明2(记住)
4.创建一个对象时,在一个类调用顺序是:(重点记住)
- 调用静态代码块和静态属性的初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们的定义顺序调用)
- 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按他们的定义顺序调用)、
- 调用构造器
package JAVA面向对象高级部分.static_.codeBlock;
public class CodeBlockDetail02 {
public static void main(String[] args) {
//System.out.println(A1.number);
/**
* 返回值是:
* 我是getNumber()的静态方法
* 我是A1的静态代码块
* 100
*/
A1 a1 = new A1();
/**
* 返回值:
* 我是getNumber()的静态方法
* 我是A1的静态代码块
* 我是getSum()非静态方法
* 我是A1类的非静态代码块
* 我是A1的构造器,初始化最后!!!
*/
}
}
class A1 {
//非静态属性初始化
public int sum = getSum();
//静态属性的初始化
public static int number = getNumber();
//非静态属性初始化
public int getSum(){
System.out.println("我是getSum()非静态方法");
return 200;
}
//非静态代码块
{
System.out.println("我是A1类的非静态代码块");
}
//静态代码块
static {
System.out.println("我是A1的静态代码块");
}
public static int getNumber(){
System.out.println("我是getNumber()的静态方法");
return 100;
}
public A1(){
System.out.println("我是A1的构造器,初始化最后!!!");
}
}
细节3:(记住)
构造器前面隐含的super
关键和调用普通代码块(顺序是super
关键字到普通代码块),创建对象的入口是构造器,构造器前面隐含的super
和普通代码块决定了执行的顺序
构造器的最前面其实隐含了super()
和调用普通代码块,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的CodeBlockDetail03.java
package JAVA面向对象高级部分.static_.codeBlock;
public class CodeBlockDetail03 {
public static void main(String[] args) {
Cat cat = new Cat();//创建对象的入口是从构造器的
/**
* 返回值:
* 我是Animal类的非静态代码块
* 我是Animal类的无参构造器
* 我是Cat类的非静态代码块
* 我是Cat类的无参构造器
*/
}
}
/**
* 构造器的最前面其实隐含了super()和调用本类普通代码块,静态相关的代码块,
* 属性初始化,在类加载时,就执行完毕了,因此是优先于构造器和普通代码块的执行
*/
class Animal {
{
System.out.println("我是Animal类的非静态代码块");
}
//Animal类的无参构造器
public Animal(){
//这里隐含了
super();//调用父类的无参构造器
//这里还隐含了调用本类的非静态代码块
System.out.println("我是Animal类的无参构造器");
}
}
class Cat extends Animal{
{
System.out.println("我是Cat类的非静态代码块");
}
public Cat(){
//这里隐含了
super();//调用父类的无参构造器
//这是还隐含了调用本类的非静态代码块
System.out.println("我是Cat类的非静态代码块");
}
}
细节4(记住)(必须理解下面的代码)
创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
- 父类的静态代码块和静态属性(优先级一样,按定义顺序执行):注意没有初始化静态方法因为静态是需要调用才会执行
- 子类的静态代码块和静态属性(优先级一新,按定义顺序执行):注意没有初始化静态方法因为静态是需要调用才会执行
- 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行):注意没有初始化静态方法因为静态是需要调用才会执行
- 父类的构造器
- 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行):注意没有初始化静态方法因为静态是需要调用才会执行
- 子类的构造器
继承关系的执行顺序:(1)父类的静态代码块和静态属性的初始化,子类的静态代码块和静态属性的初始化(2)父类普通代码块和普通属性的初始化到父类的构造器 (3)子类普通代码块和普通属性初始化到子类的构造器
父子静态 --》 父类普通 --》父类构造器 --》子类普通 --》子类构造器
小结:在继承关系下,创建对象分两个大步:
第一大步是:先加载父类和子类信息
- 先加载父类的静态代码块和静态属性初始化:注意没有初始化静态方法因为静态方法是需要调用才会执行
- 再加载子类的静态代码块和静态属性初始化:注意没有初始化静态方法因为静态方法是需要调用才会执行
第二大步是:再创建子类对象,创建对象的入口是从构造器开始,构造器前面默认调用 父类(使用super
关键字)代码块和普通属性的初始化
- 父类的普通代码块和普通属性初始化:注意没有初始化静态方法因为静态是需要调用才会执行
- 父类的构造器
- 子类的普通代码块和普通属性初始化:注意没有初始化静态方法因为静态是需要调用才会执行
- 子类的构造器
静态方法是调用时才会被执行的
细节5:
静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员
package JAVA面向对象高级部分.static_.codeBlock;
public class CodeBlockDetail04 {
public static void main(String[] args) {
//1.先进行父类的加载
//2.再创建对象,是从子类的构造器开始的
new Student();
/**
* 返回值
* 我是Person类一个getVal01方法
* 我是Person类一个静态代码块
* 我是Student类的getVal03方法
* 我是Student类的静态代码块
* 我是Person类一个getVal02方法
* 我是Person类一个普通代码块
* 我是Person类的无参构造器
* 我是Student类的getVal04方法
* 我是Student类的非静态代码块
* 我是Student类的无参构造器
*/
}
}
class Person {
//注意是静态方法是调用才会被执行的
private static int n1 = getVal01();
public int n2 = getVal02();
static {
System.out.println("我是Person类一个静态代码块");//(2)
}
{
System.out.println("我是Person类一个普通代码块");//(6)
}
public static int getVal01(){
System.out.println("我是Person类一个getVal01方法");//(1)
return 10;
}
public int getVal02(){
System.out.println("我是Person类一个getVal02方法");//(5)
return 20;
}
//构造器
public Person(){
//1.隐含了super()
//2.隐含了普通代码和普通属性的初始化
System.out.println("我是Person类的无参构造器");//(7)
}
}
class Student extends Person {
private static int n3 = getVal03();
public int n4 = getVal04();
static {
System.out.println("我是Student类的静态代码块");//(4)
}
{
System.out.println("我是Student类的非静态代码块");//(9)
}
public static int getVal03(){
System.out.println("我是Student类的getVal03方法");//(3)
return 30;
}
public int getVal04(){
System.out.println("我是Student类的getVal04方法");//(8)
return 40;
}
public Student(){
//1.这里隐含了super()语句
//2.还普通代码块和普通属性的初始化
System.out.println("我是Student类的无参构造器");
}
}
练习:
class Person {
public static int total;
static {
total = 100;
System.out.println("in static block!");
}
}
public cals Test{
public static void main(String[] args){
System.out.println("total="+Person.total);
System.out.println("total="+Person.total);
}
}
/**
*返回值:
* in static block!
* total=100
* total=100
*/
练习2
class Sample{
Sample(String s){
System.out.println(s);
}
Sample(){
System.out.println("Sample默认构造器函数被调用");
}
}
class Test{
Sample sam1 = new Sample("sam1成员初始化");
static Sample sam = new Sample("静态成员sam初始化");
static {
System.out.println("static块执行");
if(sam == null)
System.out.println("sam is null");
}
Test(){
System.out.println("Test默认构造器被调用");
}
}
public static void main(String[] str[]){
Test a = new Test();
}
/**
* 返回值:
* 静态成员sam初始化
* static块执行了
* sam1成员初始化
* Test默认构造函数被调用
对象创建步骤:
- 加载类的信息(类信息的加载会倒致静态代码块和静态属性的初始化,加载类信息的三种方式:1.创建对象 2.创建子类对象(父类的类信息也会加载)3.静态属性或静态方法的调用)
- 在堆中开辟空间,并对属性初始化(构造器的调用会调用
super
语句和普通代码块的执行) - 将堆中的地址赋给方法区中的引用