一、代码块
(一)*字段不存在多态的特征
通过对象调用字段,在编译时期就已经决定了调用哪一块的内存空间的数据。字段不存在覆盖的概念,在多态时,不能有多态的特征(在运行时期体现子类的特征)。只有方法才有覆盖的概念。
当子类和父类存在相同字段的时候,无论修饰符是什么,都会在各自的内存空间中存储数据。
(二) 什么是代码块
在类或者方法中直接使用“{}”括起来的一段代码,表示一块代码区域。
代码里的变量属于局部变量,只在自己所在的区域(前后的{})内有效。
根据代码块定义的位置不同,我们又分成三种形式:
(三) 局部代码块:直接定义在方法内部的代码块:
一般的情况下,我们不会直接使用局部代码块。会结合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类等。
三、基本类型包装类
(一) 装箱和拆箱
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
八大基本类型的包装类都是使用了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())
}
}
反馈与建议
- 博客:我的优快云博客
- 邮箱:pengtdy@qq.com
- 我的技术博客群:
644402856
感谢你阅读这篇博客。如果您喜欢这篇博客就请关注我和朋友一起分享吧!