Java基础-第六章(面向对象4)

本文详细介绍了Java中的代码块,包括局部代码块、初始化代码块和静态代码块的使用。此外,讲解了final修饰符在类、方法和变量上的作用,并探讨了final类和方法的场景。还详细阐述了基本类型包装类的装箱、拆箱、享元模式及Integer与int的区别。最后讨论了抽象类和模板方法设计模式的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、代码块

(一)*字段不存在多态的特征

通过对象调用字段,在编译时期就已经决定了调用哪一块的内存空间的数据。字段不存在覆盖的概念,在多态时,不能有多态的特征(在运行时期体现子类的特征)。只有方法才有覆盖的概念。

当子类和父类存在相同字段的时候,无论修饰符是什么,都会在各自的内存空间中存储数据。

(二) 什么是代码块

在类或者方法中直接使用“{}”括起来的一段代码,表示一块代码区域。
代码里的变量属于局部变量,只在自己所在的区域(前后的{})内有效。

根据代码块定义的位置不同,我们又分成三种形式:

(三) 局部代码块:直接定义在方法内部的代码块:

一般的情况下,我们不会直接使用局部代码块。会结合if、while、for、try等关键字联合来表示一块代码区域。

(四) 初始化代码块(构造代码块):直接定义在类中:

每次创建对象的时候都会执行初始化代码块:
每次创建对象的时候都会调用构造器,在调用构造器之前,会先执行本类中的初始化代码块(如下代码)。

// 编译前
class CodeBlockDemo{
    {
        System.out.println("初始化代码块");
    }
    CodeBlockDemo(){
        System.out.println("构造器...");
    }
}

// 编译后
class CodeBlockDemo{
    CodeBlockDemo(){
        System.out.println("初始化代码块");
        System.out.println("构造器...");
    }
}

通过反编译之后,我们发现初始化代码块也作为构造器的最初语句。
在平时的开发中,一般不使用初始化代码块。即使要做初始化操作,一般在构造器中做即可。如果需要初始化的代码较多,构造器的结构比较混乱,此时可以专门定义一个方法做初始化操作,再在构造器中调用即可。

(五) 静态代码块:使用static修饰的初始化代码块:

在主方法执行之前执行静态代码块,而且只执行一次。

main方法是程序的入口,为什么静态代码块会优先于mian方法执行?
静态成员随着字节码的加载而加载进JVM,此时main方法还没有被执行,因为方法需要JVM调用。
先把字节码加载进JVM,而后JVM再调用main方法。

静态代码块一般,我们用来做初始化操作,加载资源、加载配置文件等。

二、final修饰符

(一) 理解final

final本省的含义是“最终的,不可改变的”,他可以修饰非抽象类,非抽象方法和变量。注意:构造方法不能使用final修饰,因为构造方法不能被继承,一定是最终的。

(二) final类

final修饰的类:表示最终类,该类不能再有子类。

只要满足以下条件就可以把一个类设计成final类:
① 某类不是专门为继承而设计的。
② 出于安全考虑,类的实现细节不允许改动,不准修改源代码。
③ 确定该类不会再被扩展。

Java里final修饰的类有很多,比如八大基本数据类型包装类和String等。

(三) final方法

final修饰的方法:最终的方法,该方法不能被子类覆盖。

什么时候方法需要final修饰:
① 在父类中提供的统一的算法骨架,不准子类通过方法覆盖来修改,此时使用final修饰(模板方法设计模式)。
② 在构造器中调用的方法(初始化方法),此时一般使用final修饰。

注意: final修饰的方法,子类可以调用,但是不能覆盖。

(四) final变量

final修饰的变量:表示常量,只能被赋值一次,不能再次赋值。

① final变量必须显示的指定初始值,系统不会为final字段初始化。
② final变量一旦赋予初始值,就不能被重新赋值。
③ 常量名规范:常量名符合标识符,单词全部使用大写字母,如果是多个单词组成,单词间使用下划线隔开。

int类型的最大值:final int MAX_VALUE = …;

补充概念:全局静态常量:public static final 修饰的变量,直接使用类名调用即可。

重点:
final修饰基本类型变量:表示该变量的值不能改变,即不能用“=”号重新赋值。
final修饰引用类型变量:表示该变量的引用地址不能改变,而不是引用的地址里的内容不能改变。

final是唯一可以修饰局部变量的修饰符。局部内部类只能访问final修饰的局部变量。

1.什么时候使用常量

当程序中,多个地方使用到共同的数据,且数据不会发生改变,此时我们专门定义全局的常量。

一般我们在开发中会专门定义一个常量类,专门用来存储常量数据。

常量分类:
① 字面值常量(直接给出的数据值/直接量):比如:整数常量1、2、3,小数常量:3.14,布尔常量false、true等。
② 定义的final变量。

(五) 单例设计模式

设计模式(Design pattern):是一套被反复使用,多数人知晓的,经过分类编目的,代码设计经验的总结。使用设计模式是为了可重用代码,让代码更容易被他人理解,保证代码的可靠性。设计模式使代码编制真正工程化。设计模式是软件工程的基石脉络。

单例设计模式(Singleton):最常用,最简单的设计模式,单例的编写有N种方法。
目的:保证某一个在整个应用中的某一个类有且只有一个实例(一个类在堆内存中只存在一个对象),即所有指向该类型的实例引用,都是指向同一块内存空间。

写单例模式的步骤:(饿汉式)
① 必须在该类中,自己先创建出一个对象。
② 私有化自身的构造器,防止外界通过构造器创建新的对象。
③ 向外暴露一个公共的静态方法用于获取自身的对象。

class ArrayUtil{
    // ① 必须在该类中,自己先创建出一个对象。
    private static ArrayUtil instance = new ArrayUtil();
    // ② 私有化自身的构造器,防止外界通过构造器创建新的对象。
    private ArrayUtil(){}
    // ③ 向外暴露一个公共的静态方法用于获取自身的对象。
    public static ArrayUtil get Instance(){
        return instance;
    }
}

工具类的设计:
工具类:存放了某一类事物的工具方法的类。、
工具类存放的包:工具包(util,utils,tool,tools)存放工具类。
工具类起名:XxxUtil、XxxUtils等,表示一类事物。

工具类的设计:工具在开发中其实只需要存在一份即可。
① 如果工具方法没有使用static修饰,说明工具方法要使用工具类的对象来调用。此时把工具类设计成单例的。
② 如果工具方法全部使用static修饰,说明工具方法只需要使用工具类名调用即可。此时必须把工具类的构造器私有化(防止创建工具类对象调用静态方法)。

一般的首选第二种,在JDK中提供的工具方法都是第二种。如:java,util,Array类等。

三、基本类型包装类

(一) 装箱和拆箱
基本数据类型包装类
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
booleanBoolean

八大基本类型的包装类都是使用了final修饰,都是最终类,都不能被继承。

在Java的集合框架中,只能存储对象,不能存储基本数据类型值。

装箱:把基本的数据类型转成对应的包装类对象。
拆箱:把包装类对象转成对应的基本数据类型数据。

Sun公司从Java5开始提供了自动装箱(Autoboxing)和自动拆箱(AutoUnboxing)功能。

自动装箱:可把一个基本数据类型变量直接赋给对应的包装类变量。
自动拆箱:允许把包装类对象直接赋给对应的基本数据类型变量。

自动装箱和自动拆箱也是一种语法糖/编译器级别的新特性。
在底层依然是手动装箱和手动拆箱操作。
但是:装箱操作使用的是Integer.valueOf的方式而不是直接使用new Integer。

解释:Object obj = 17;
① 自动装箱:Integer i = 17;
② 引用的自动类型转换,把子类对象赋给父类变量:Object obj = i;

Object可以接收一切类型的值。
Object数组:Object[] 该数组可以装一切数据类型。
Object[] arr = {“A”,1,123,true};

(二) 基本使用
1.包装类中的常量

MAX_VALUE/MIN_VALUE/SIZE(在内存中存储占多少位)。
TYPE(对应的基本数据类型)。

2.包装类的构造器。xxx类型的包装类Xxx:(xxx表示8大基本数据类型)

Xxx (xxx value):接收自己的基本类型,如Integer (int val) / Boolean (boolean val)。
Xxx (String value):但是,character除外。
构造器的作用:创建包装类对象。

3.基本类型和包装类型的转换(装箱和拆箱)

装箱:

Integer i1 = new Integer(123);
Integer i2 = Integer.valueOf(123); // 推荐,带有缓存

拆箱:

int val = i1.intValue();
4.String和基本数据类型/包装类之间的转换操作

String和int/Integer之间的转换操作:

把String转换为包装类型:
方式一:static Xxx valueOf(String str):把String转换为包装类的对象。

Integer i1 = Integer.valueOf("123");

方式二:new Xxx(String str):

Integer i2 = new Integer("123");

把包装类对象转换为String。

String str = 任何对象.toString();

把基本类型转换为String。

String str = 10 + ""; // 第一种
String str = String.valueOf(10);

把String转换为基本数据类型。

static xxx.parseXxx(String str); // xxx:表示8大基本数据类型

String input = "1234";
int num = Integer.parseInt(input);

布尔类型的转换。

Boolean b = new Boolean("SS"); // false

布尔类型的转换,只会认可true,其他的都是返回的false;

(三) 享元模式

包装类中的缓存设计(享元模式):
Byte、Short、Integer、Long、:缓存[-128,127]区间的数据。
character:缓存[0,127]区间的数据。

Integer i1 = new Integer("123");
Integer i2 = new Integer("123");
System.out.println(i1 == i2); // false

Integer i3 = Integer.valueOf("123");
Integer i4 = Integer.valueOf("123");
System.out.println(i3 == i4); // true,在[-128,127]范围,就获取缓存中的数据

包装类型对象之间比较操作:统统使用equals方法来做比较,比较的是包装的数据。

(四) Integer和int的区别

Integer与int的区别(包装类型和基本类型的区别):

1.默认值

int的默认值是0。
Integer的默认值是null。
推论:Integer既可以表示null,又可以表示0。

2.包装类中提供了该类型相关的很多算法操作方法

static String toBinaryString(int i):把十进制转换为二进制。
static String toOctalString(int i):把十进制转换为八进制。
static String toHexString(int i):把十进制转换为十六进制。

3.在集合框架中,只能存储对象类型,不能存储基本数据类型值。
4.请问:Integer和int是相同的数据类型吗?不是:
// 在同一个类中,二者可以共存
public void ooxx(int val){}
public void ooxx(Integer val){}
5.方法中的基本数据类型变量存储在栈中,包装类型存储在堆中。

开发中建议使用包装类型,局部变量建议使用基本数据类型。

四、抽象类

使用abstract修饰且没有方法体的方法,称为抽象方法:
特点:
① 使用抽象abstract修饰方法没有方法体,留给子类去实现/覆盖。
② 抽象方法修饰符不能使用private、final和static修饰,因为抽象类必须有子类。
③ 抽象方法必须定义在抽象类或者接口中。
一般习惯:把abstract写在方法修饰符的最前面,这样一看就知道是抽象方法。

使用abstract关键字修饰的类:
特点:
① 不能创建实例,即不能使用new出一个抽象类,即使创建出抽象类对象,调用抽象方法,根本没有方法体。
② 可以不包含抽象方法,若包含,该类必须为抽象类,抽象类可以包含普通方法(留给子类调用的),抽象类是有构造器的,子类构造器必须先调用父类构造器。
③ 若子类没有实现/覆盖父类的所有抽象方法,那么子类也得作为抽象类(抽象派生类)。
④ 构造方法不能都定义成私有的,否则不能有子类(创建子类对象前,先调用父类构造方法)。
⑤ 抽象类不能使用final修饰。因为必须有子类,抽象方法才能得以实现。
⑥ 是不完整的类,需作为父类(必须要有子类),功能才得以实现。

抽象类:一般起名习惯使用Abstract作为前缀,让调用者一看就知道是抽象类。
抽象类中可以不存在抽象方法,如果是这样没有太大意义,但是可以防止外界创建对象,所以我们会发现有些工具类没有抽象方法,但是使用了abstract来修饰类。

抽象类不能实例化:不能使用new创建对象。

抽象类和普通类的区别:
普通类有的成员(方法、字段、构造器),抽象类都有。
抽象类不能创建对象,抽象类中可以包含抽象方法。

五、模板方法设计模式

在父类的一个方法中定义一个总体的算法的骨架(模板方法),而将某些步骤延迟到子类中,因为不同的子类实现细节不同。模板设计方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

抽象父类负责定义操作中的业务骨架,而把具体的实现步骤延迟到子类中实现。
抽象父类至少提供的方法:
模板方法:一个通用的处理方式,即模板(总体算法的骨架)。
抽象方法:一种具体的业务功能实现,由子类完成。

注意: 抽象父类提供的模板方法只是定义了一个通用的算法,其实现必须依赖子类的辅助。

模板方法作为模板样式不准子类覆写,使用final修饰。

// 操作的模板类
abstract class AbstractOperateTimeTemplate{
    // 模板方法:总体算法的骨架,子类不能修改
    final public long getTotalTime(){
        // 开始时间
        long begin = System.currentTimeMillis();
        // 具体操作(留给子类去完成)
        this.doWork();
        // 结束时间
        long end = System.currentTimeMillis();
        // 时间差
        long time = end - begin;
        return time;
    }
    // 具体操作
    protected abstract void doWork();
}

// 子类完成
class StringOperate extends AbstractOperateTemplate{
    protected void doWork(){
        String str = "";
        for (int i = 0;i < 10000;i++){
            str += i;
        }
    }
}

// 最终调用
class TemplateMethodDemo{
    public static void main(String args){
        // 创建子类对象,调用父类方法
        System.out.println(new StringOperate().getTotalTime())
    }
}

反馈与建议

感谢你阅读这篇博客。如果您喜欢这篇博客就请关注我和朋友一起分享吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值