面向对象系列——特殊的引用类型(与class并列地位)
文章目录
- 面向对象系列——特殊的引用类型(与class并列地位)
- 特殊的:接口、枚举、注解、记录,包装类
- 一、interface接口(不是类)—提供一种规范(具有多态性)
- 1.1 接口&理解
- 1.2 区分抽象类和接口
- 1.3 JDK8中&JDK9关于接口的新特性
- 2.4 接口的总结与面试题(便于理解接口)
- 二、Enum枚举类
- 三、Annotation注解
- 3.1 注解介绍&常用注解
- 3.2 元注解
- 3.3 自定义注解和使用
- 3.4 JUnit单元测试
- 四、包装类
- 4.1 包装类的介绍&理解
- 4.2 包装类与基本数据类型的相互转换
- 4.3 String类型与包装类&基本类型的相互转换
- 五、Record记录(暂时不看)
- 六、企业真题
特殊的:接口、枚举、注解、记录,包装类
数据类型分类:基本数据类型(char、byte、short、int、long、float、double、)、引用数据类型(String、数组[]、类class、接口interface、枚举enum、注解@interface、记录)
在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口。
一、interface接口(不是类)—提供一种规范(具有多态性)
接口的本质是契约、标准、规范,就像我们的法律一样。制定好后大家都要遵守。
比如:Java程序是否能够连接使用某种数据库产品,那么要看该数据库产品能否实现Java设计的JDBC规范(JDBC就是一堆API接口),对于Java程序不要面向数据库编程,而是面向接口编程。不同的数据库厂商都满足这样的一套标准,使得代码具有一致性。因此我们在使用的适合只需要创建不同的对象然后利用接口的多态性,调用相关的接口方法即可
1.1 接口&理解
1、接口定义:接口就是规范,定义的是一组规则。接口与实现:has-a关系,子父类extends继承:is-a关系,abstract类中的部分实现关系。
主要记住:公共的静态常量、公共的抽象方法(只能使用子类对象调用),JDK8以后会有新特性
特点:使用interface修饰,会被编译成.class文件,但它不是类,而是另外一种引用类型。要有子类实现才行,如果子类既有继承又有实现,则先写继承后写实现。
2、结构的内部结构:
- 属性:必须使用public static final修饰,即静态常量,可以省略,所有的属性声明都是一致的。final不能改,static 只能被接口调用/实现接口的类/或其对象
- 方法:JDK8之前:声明抽象方法,修饰为public abstract,即抽象方法(非静态),可以省略,默认就是这个,abstract表示该方法必须要被实现(即重写),除了使用抽象类实现它。只能使用子类的对象调用。
新特性:JDK8:可以声明静态方法、默认方法;JDK9:可以声明私有方法 - 不能声明:构造器、代码块等,因为接口中没有成员需要动态初始化。
3、接口的使用规则:
- 类实现接口:类必须实现接口中所有抽象方法,否则该类必须为抽象类
- 接口的多实现:类可以实现多个接口,一定程度上弥补了类的单继承的局限性。
- 接口的多继承:接口可以继承接口且可以多继承,那么当子类实现该接口时,必须要实现他继承的所有接口的抽象方法
接口与实现类构成多态引用
:对比类的多态性:父类 变量名 = new 子类(),这个父类也可以new,但是接口不能new。
接口 变量名 = new 子类,通过接口调用子类中的方法,本质是一个虚方法调用,只不过这里就不存在可能在子类中没有重写的情况了,因为接口中的方法abstract在子类中必须要实现(重写)。
接口的定义与实现格式、接口的多实现、接口的多继承
接口结构:静态常量、抽象方法
public interface C {
public static final int a = 2;//可以省略public static final
int b = 4;
public abstract int add();//可以省略public abstract
int add();
}
//A是SuperA的子类,是B、C的实现类
class A extends SuperA implements B,C{
}
//接口的多实现
【修饰符】 class 实现类 extends 父类 implements 接口1,接口2,接口3。。。{
// 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
//接口的多继承
public interface UsbC extends Chargeable,USB3 {
void reverse();
}
1.2 区分抽象类和接口
1、区分抽象类和接口
抽象类:可以定义抽象方法、普通方法;不能被实例化;有构造器;属性可以任意
接口:只能定义公共的抽象方法;不能被实例化;没有构造器;属性必须是final static
类与类是单继承,类与接口是多实现,接口与接口是多继承
1.3 JDK8中&JDK9关于接口的新特性
1、接口内部结构:静态常量、抽象方法,JDK8:可以声明公共的静态方法、公共的默认方法;JDK9可以声明私有方法
- 公共的静态方法:只能通过"接口."调用,其他方式都不行,这也是静态方法的特性。
- 公共的默认方法:可以被实现类继承,如果没有重写,默认调用接口中声明的默认方法,如果重写了,则是实现类重写的方法(虚方法)
- 定义私有方法:接口自己用,给接口中的默认方法用
(1)接口冲突:当一个类实现多个接口,而多个父接口中包含了方法签名相同(同名同参数)的默认方法,则实现类在没有重写这些接口的默认方法的情况下,会报错,该怎么办呢?抽象方法相同是不会出现这样的问题,因为必然要重写方法。
要求:此时实现类必须要重写接口中定义的同名同参数的默认方法
注意:子接口重写默认方法时,default关键字可以保留。子类重写默认方法时,default关键字不可以保留。
(2)类优先原则:当一个类继承父类,又实现若干接口时,父类成员方法和接口中的默认方法同名同参数时,子类就近选择执行父类的成员方法。
(3)在子类(实现类)中调用实现接口中被重写的默认方法 “接口名.super.方法名
"(因为默认方法不是静态的因此需要这样调用),调用父类的方法和之前一样"super.方法名"
(4)常量冲突问题
- 当子类继承父类又实现父接口,而父类中存在与父接口常量同名的成员变量,并且该成员变量名在子类中仍然可见。
- 当子类同时实现多个接口,而多个接口存在相同同名常量。
调用方式:父类"父类.常量"、接口"接口.常量",如果没有重名的话就可以直接访问。
public interface CompareA{
//接口中的静态方法,只能通过接口来调用。不能重写
public static void method0(){
System.out.println("JDK8:CompareA接口中定义的静态方法");
}
public default void method1(){
System.out.println("JDK8:CompareA接口中定义的默认方法method1()");
}
//接口中的默认方法:如果不加default会认为它是一个抽象方法。
public default void method2(){
System.out.println("JDK8:CompareA接口中定义的默认方法method2()");
}
//默认就是抽象方法:public abstract,子类必须要重写
int add();
}
//定义接口
public interface CompareB{
//接口中的静态方法,只能通过接口来调用。不能重写
public static void method1(){
System.out.println("JDK8:CompareB接口中定义的静态方法");
}
private void method5(){
System.out.println("JDK9:我是接口中定义的私有方法");
}
}
class SuperClas{//父类中与接口CompareA的默认方法同名同参数,子类在继承时遵循类优先原则
public void method2(){
System.out.println("JDK8:类SuperClas中定义的方法method2()");
}
}
//子类实现
class A extends SuperClas implements CompareA,CompareB{//类A同时实现两个接口,这两个接口中有同的默认方法method1,必须要重写
@Override
public void method1(){//两个接口有重名的默认方法必须要重写
//法一1保留其中一个父接口
//CompareA.super.method1();
//法2完全重写
System.out.print("子类重写两个接口同名同参数的methond1()方法")
}
//默认方法可以重写,也可以不重写
public void method2(){
}
//重写实现抽象方法
public int add(){
}
}
2.4 接口的总结与面试题(便于理解接口)
- 接口本身不能创建对象,只能创建接口的实现类对象,接口类型的变量可以与实现类对象构成多态引用。
- 声明接口用interface,接口的成员声明有限制:
- (1)公共的静态常量
- (2)公共的抽象方法
- (3)公共的默认方法(JDK8.0 及以上)
- (4)公共的静态方法(JDK8.0 及以上)
- (5)私有方法(JDK9.0 及以上)
- 类可以实现接口,关键字是implements,而且支持多实现。如果实现类不是抽象类,就必须实现接口中所有的抽象方法。如果实现类既要继承父类又要实现父接口,那么继承(extends)在前,实现(implements)在后。
- 接口可以继承接口,关键字是extends,而且支持多继承。
- 接口的默认方法可以选择重写或不重写。如果有冲突问题,另行处理。子类重写父接口的默认方法,要去掉default,子接口重写父接口的默认方法,不要去掉default。
- 接口的静态方法不能被继承,也不能被重写。接口的静态方法只能通过“接口名.静态方法名”进行调用。
面试题
1、为什么接口中只能声明公共的静态的常量?
因为接口是标准规范,那么在规范中需要声明一些底线边界值,当实现者在实现这些规范时,不能去随意修改和触碰这些底线,否则就有“危险”。
例如:USB1.0规范中规定最大传输速率是1.5Mbps,最大输出电流是5V/500mA
USB3.0规范中规定最大传输速率是5Gbps(500MB/s),最大输出电流是5V/900mA
例如:尚硅谷学生行为规范中规定学员,早上8:25之前进班,晚上21:30之后离开等等。
2、为什么JDK8.0 之后允许接口定义静态方法和默认方法呢?因为它违反了接口作为一个抽象标准定义的概念。
静态方法
:因为之前的标准类库设计中,有很多Collection/Colletions或者Path/Paths这样成对的接口和类,后面的类中都是静态方法,而这些静态方法都是为前面的接口服务的,那么这样设计一对API,不如把静态方法直接定义到接口中使用和维护更方便。
默认方法
:(1)我们要在已有的老版接口中提供新方法时,如果添加抽象方法,就会涉及到原来使用这些接口的类就会有问题,那么为了保持与旧版本代码的兼容性,只能允许在接口中定义默认方法实现。比如:Java8中对Collection、List、Comparator等接口提供了丰富的默认方法。(2)当我们接口的某个抽象方法,在很多实现类中的实现代码是一样的,此时将这个抽象方法设计为默认方法更为合适,那么实现类就可以选择重写,也可以选择不重写。
3、为什么JDK1.9要允许接口定义私有方法呢?因为我们说接口是规范,规范是需要公开让大家遵守的。
私有方法:因为有了默认方法和静态方法这样具有具体实现的方法,那么就可能出现多个方法由共同的代码可以抽取,而这些共同的代码抽取出来的方法又只希望在接口内部使用,所以就增加了私有方法。
二、Enum枚举类
特点:外部不能创建类、内部几个有限固定的对象(常量)、内部的实例变量最好是私有化常量
。固定、常量,普通的类需要我们自己new并初始化对象,而枚举类是在内部定义好几个常量对象了,你调用就行了,且不能创建对象。
涉及到的变量都定义为final常量
1、定义:枚举类型本质上也是一种类,只不过是这个类的对象是有限的
(在类内部public static final全局不可变),不能让用户随意创建(私有化构造器private),实例变量也不能获取(private final),实例变量是建议也可以获取。
举例:季节:Spring(春节)......Winter(冬天);
星期`:Monday(星期一)…Sunday(星期天)
2、开发中建议:如果针对某个类,其实例是确定个数的,则推荐将此类声明为枚举类,如果枚举类的实例只有一个,则可以看作是单例的实现方式;当需要定义一组常量时推荐枚举类。
3、如何定义枚举类
- JDK5.0之前,需要自定义枚举类
- 构造器私有化:确保不能在类的外部创建其对象
- 创建枚举类的几个固定的常量对象:声明为public static final→在外部"类名.该实例对象.get方法"
- 实例变量:建议private final,在构造器中初始化,提供get方法获取其值
总结:私有化构造器、实例常量,用户在外部通过类调用这几个有限的常量对象
- JDK5.0以后,支持enum关键字来快速定义枚举类
- 区别就是创建常量对象直接列出"常量对象列表"(必须在首行),系统会自动添加 public static final 修饰。
- 枚举类默认继承的是java.alng.Enum类,不能再继承其他的类型;JDK5.0之后case后面可以写枚举类常量名。
- 可以实现implements接口:①枚举类中实现接口中的抽象方法,当通过不同的枚举类对象调用此方法时执行的是同一个方法;②常量对象内重写/实现抽象方法:表现形式每个枚举值(常量对象)在调用实现的接口呈现出不同的行为方式,即每个枚举类的常量对象对于实现的接口方法有不同的行为——理解为匿名内部类
开发中使用枚举类:定义这样一个枚举类,在使用的时候将某个变量定义为枚举类,在"枚举类.常量对象"得到的是这个常量对象的名字而不是地址什么的。
enum A{
//常量对象列表
常量对象1,常量对象2,常量对象3;
}
枚举类实现接口的语法格式
enum A implements 接口1,接口2{
//常量对象列表
//实例常量
//实现接口中的抽象方法
}
enum A implements 接口1,接口2{
常量对象名1([参数列表]){
//实现重写接口中的方法
//抽象方法的实现
},//注意这里是逗号
常量对象名2([参数列表]){
//实现重写接口中的方法
//抽象方法的实现
};//注意最后一个常量对象是分号结尾!
}
4、Enum中常见的方法:使用Enum定义的枚举类,默认父类是java.alng.Enum类,不能再继承其他的类型否则会报错。
- String toString(): 默认返回的是常量名(对象名),可重写
- String name():得到当前枚举常量的名称。优先使用toString()。"Season.SPRING,name()"得到常量对象名SPRING
- static 枚举类型[] values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值(即所有的常量对象)。
- static 枚举类型 valueOf(String name):把枚举类常量对象名字符串转换为枚举类对象。如不是枚举类中已存在的常量对象名,会有运行时异常:IllegalArgumentException。
- int ordinal():返回当前枚举常量的次序号,默认从0开始。“Season.SPRING,ordinal()”
JDK5.0之前使用class定义枚举类:私有化常量、私有化构造器并初始化常量、创建几个固定的静态常量对象
Class Season implements Info{
//声明当前类的实例常量,使用private finale修饰
private final String SEASONNAME;//私有化常量
private final String SEASONDESC;
//私有化构造器
private Season(String name, String desc){
this.SEASONNAME=name;
this.SEASONDESC=name;
}
//内部提供几个固定的对象:静态的,不可变的常量对象
public static final Season SPRING = new Season("春天","春暖花开");
public static final Season SUMMER = new Season("夏天","夏日炎炎");
public static final Season AUTUMN = new Season("秋天","秋高气爽");
public static final Season WINER = new Season("冬天","白雪皑皑");
public String getSeasonName(){
return this.SEASONNAME;
}
public String getSeasonDesc(){
return this.SEASONDESC;
}
//这里重写父类是Object
@Override
public String toString() {
return "Season{" +
"SEASONNAME='" + SEASONNAME + '\'' +
", SEASONDESC='" + SEASONDESC + '\'' +
'}';
}
}
JDK5.0以后通关关键字enum创建枚举类
interface Info{
void show();
}
enum Season{
//1、必须在枚举类的开头声明多个对象,对象之间使用逗号隔开
//系统会自动添加 public static final 修饰
SPRING("春天","春暖花开"){
public void show(){
System.out.println("春天在哪里?");
}
},
SUMMER("夏天","夏日炎炎"){
public void show(){
System.out.println("宁静的夏天");
}
},
AUTUMN("秋天","秋高气爽"){
public void show(){
System.out.println("秋天是用来分手的季节");
}
},
WINER("冬天","白雪皑皑"){
public void show(){
System.out.println("2002年的第一场雪");
}
};
//2、声明当前类的实例常量,使用private finale修饰
private final String SEASONNAME;
private final String SEASONDESC;
//3、私有化构造器
private Season(String name, String desc){
this.SEASONNAME=name;
this.SEASONDESC=name;
}
//4、提供get方法
public String getSeasonName(){
return this.SEASONNAME;
}
public String getSeasonDesc(){
return this.SEASONDESC;
}
//这里重写父类是enum
@Override
public String toString() {
return "Season{" +
"SEASONNAME='" + SEASONNAME + '\'' +
", SEASONDESC='" + SEASONDESC + '\'' +
'}';
}
}
三、Annotation注解
框架 = 注解 + 反射 + 设计模式
3.1 注解介绍&常用注解
1、注解定义:从JDK5.0开始引入,“@注解名”
可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。还可以添加一些参数值,这些信息被保存在 Annotation 的 “name=value” 对中。
注解可以在类编译、运行时进行加载,体现不同的功能:用不同的元注解实现@Retention,@Target,@Inherited,@Documented
2、注解对比注释
单行/多行注释:给程序员看
注解:被编译器或其他程序读取,程序员还可以根据注解的不同做出相应的处理
3、注解的应用场景
- 生成文档相关的注解:即放在注释中的注解
- 在编译时进行格式检查:JDK内置的三个基本注解
- @Override: 检测被标记的方法为有效的重写方法,如果不是会编译报错,只能修饰方法。
- @Deprecated: 用于表示被标记的数据已经过时,可以修饰修饰 属性、方法、构造、类、包、局部变量、参数。
- @SuppressWarnings: 抑制编译器警告,可以用于修饰类、属性、方法、构造、局部变量、参数。可以指定的警告类型:unused(未使用的警告)、all(所有警告)、unchecked、deprecation、nls、null、rawtypes、static-access、static-method、super
- 跟踪代码依赖性,实现替代配置文件功能
- Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中进行Servlet的部署
- Spring框架中关于“事务”的管理
3.2 元注解
1、定义:对现有注解进行解释说明的注解
2、JDK1.5在java.lang.annotation包定义了4个标准的meta-annotation类型:使用范围、生命周期
、能否被继承、能否被生成到API文档中。
- @Target:用于描述注解的使用范围
- 通过枚举类型ElementType的10个常量对象指定:TYPE,METHOD,CONSTRUCTOR,PACKAGE, PARAMETER, LOCAL_VARIABLE, ANNOTATION_TYPE…
- @Retention:用于描述注解的生命周期
- 通过枚举类型RetentionPolicy的3个常量对象指定:SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时),
唯有RUNTIME阶段才能被反射读取到
。
- 通过枚举类型RetentionPolicy的3个常量对象指定:SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时),
- @Inherited:允许子类继承父类中的注解
- @Documented:表明这个注解应该被 javadoc工具记录。
元注解使用
package com.atguigu.annotation;
import java.lang.annotation.*;
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
3.3 自定义注解和使用
1、完整的注解包含三个部分:声明、使用、读取
- 声明注解:
- 元注解:@Retention,@Target,@Inherited,@Documented,分别说明它的声明周期,使用位置,是否被继承,是否被生成到API文档中。
- 无参有返回值的抽象方法,又称为配置参数。返回值只能是数组类型(8中基本数据类型、引用类型[String、类、枚举类enum、注解类annotation])。可以使用 default 关键字为抽象方法指定默认返回值。其中数组表示可以给多个值
- 使用注解时必须指定返回值,除非定义了默认值。格式是“方法名 = 返回值”,如果只有一个抽象方法需要赋值,且方法名为value,可以省略“value=”,所以如果注解只有一个抽象方法成员,建议使用方法名value。
- 使用注解
- @注解名(配置参数=“值”)//多个用逗号隔开
- 读取注解:自定义的注解必须配上注解的信息处理流程才有意义。自定义的注解只能使用反射的代码读取,所以自定义注解的声明周期必须是RetentionPolicy.RUNTIME。
import java.lang.annotation.*;
[元注解]
[修饰符] @interface 注解名{
[成员列表]//抽象方法
//成员列表的形式,可以使用default关键字为抽象方法指定默认值
String value() default "Hello";//因为定义了@interface类型,因此这相当于:abstract String value();
}
补充:自定义的注解有什么作用?如何使用?或者框架中提供了注解,当使用注解后它的底层原理/实现是什么样子的?
这些使用注解修饰的地方(比如类/属性),通过调用方法"类/属性.isAnnotationPresent(注解名)",判断他们是否存在指定的注解名,后续①对于加了注解的类/属性进行指定的操作,比如实例化,或者如果只是得到加了注解类得一些属性/对象,那么可以通过反射在得到一些信息;②对获取该注解的一些信息,进行一些操作。
3.4 JUnit单元测试
测试:在main方法中测试(Java程序的执行入口)、JUnit单元测试
1、测试分类
黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
白盒测试:需要写代码的。关注程序具体的执行流程。
2、JUnit测试定义:由 Erich Gamma 和 Kent Beck 编写的一个测试框架(regression testing framework),以便单元测试,属于白盒测试。
3、如何使用JUnit单元测试:每个单元测试互不影响
- 项目中引入JUnit的库JUnit.jar包,以class文件形式存在。如果有多个modules都想使用,每个module都要有。注意junit必须要是Compile
- 编写和运行@Test单元测试方法:JUnit4版本,@Test标记的方法满足的要求:方法本身必须是public,非抽象的,非静态的,void无返回值,()无参数的;所在的类必须是public的,非抽象的,包含唯一的无参构造器。
4、设置执行JUnit用例时支持控制台输入:“Help→Edit Custom VM Options…”
在idea64.exe.vmoptions配置文件中加入下面一行设置,重启idea后生效。
-Deditable.java.test.console=true
5、因为JUnit测试方法的定义基本都是一样的,因此把它定义为一个模板(比如fori可以得到for循环的语句)
“Settings→Editor→Live Templates”:选中自定义的模板组,点击”+”(1.Live Template)来定义模板。
package com.atguigu.junit;
import org.junit.Test;
//该类必须是public的,非抽象的,包含唯一的无参构造器
public class TestJUnit {
@Test
//该方法必须是public,非抽象的,非静态的,void无返回值,()无参数的
public void test01(){
System.out.println("TestJUnit.test01");
}
@Test
public void test02(){
System.out.println("TestJUnit.test02");
}
@Test
public void test03(){
System.out.println("TestJUnit.test03");
}
}
四、包装类
包装类:先去常量池中对比没有就在常量池中添加,然后再去堆中new对象,效率低一些
类型转换总结:主要记住自动装箱、自动拆箱、String的parseXXX()静态方法、valueOf(基本/包装类)方法
4.1 包装类的介绍&理解
1、包装类的定义:基本数据类型相应的引用类型就叫做包装类(封装类),有了类的特点,就可以调用类中的方法,Java才是真正的面向对象。即希望基本数据类型具备类的特征。
2、包装类与基本数据类型的相互转换?具备引用类型变量的相关特征(封装、继承、多态)
Object类的equals(Object obj)//情况1:方法形参只能比较引用类型
ArrayList类的add(Object obj)//情况2:方法形参:没有办法添加基本数据类型的数据,比如add(boolean b)
Set<T>、List<T>、Cllection<T>、Map<K,V>//情况3:泛型
为什么包装类型→基本数据类型?
对象没有办法进行运算
3、注意点:基本数据类型与包装类的默认值是有区别的
比如:boolean默认值是false,Boolean默认值是null,因为包装类是本质是对象!
4、常用的包装类的方法
静态方法:ValueOf(基本数据类型):基本→包装类型,可以是数值型字符串类型,并且boolean忽略大小写
非静态方法:包装类/对象.xxxValue():包装类→基本
静态方法:parseXXX(字符串类型):String→包装类型
静态方法:包装类/对象.toString():包装类→String
String类中的静态方法:valueOf(包装类/基本):包装类/基本→String类
包装类中的API:数据类型的MAX_VALUE和MIN_VALUE、字符串的大小写toUpperCase()、toLowerCase、整数转进制toBinaryString()【Hex、Octal】、比较的方法campare()。
5、包装类的特点:包装类的对象不可变、包装类缓存对象
包装类的缓存对象如下:只有浮点类型没有
包装类 | 缓存对象 |
---|---|
Byte | -128~127 |
Short | -128~127 |
Integer | -128~127 |
Long | -128~127 |
Float | 没有 |
Double | 没有 |
Character | 0~127 |
Boolean | true和false |
包装类中自动装箱的静态方法valueOf(基本数据类型)源码
享元的设计模式:把公用的对象放在一个数组中,不需要每次都new
public static Integer value0f(int i){
if(i >= IntegerCache.low && i <= IntegerCache.high)//low=-128, high=127
return IntegerCache.cache[i+(-IntegerCache.low)];//在[-128.127]范围内是在现有的数组中取的
return new Integer(i);//如果不在就是新new的包装类对象
}
包装类缓存对象存在后的一些常见包装类对象值的问题
Integer a = 1;
Integer b = 1;
System.out.println(a == b);//true
Integer i = 128;
Integer j = 128;
System.out.println(i == j);//false
Integer m = new Integer(1);//新new的在堆中
Integer n = 1;//这个用的是缓冲的常量对象,在方法区
System.out.println(m == n);//false
Integer x = new Integer(1);//新new的在堆中
Integer y = new Integer(1);//另一个新new的在堆中
System.out.println(x == y);//false
6、包装类的应用题示例:使用包装类,能用Vector中的addElement()方法添加这个基本数据类型的数据
利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出最高分,并输出学生成绩等级。
- 提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的长度。而向量类java.util.Vector可以根据需要动态伸缩。
- 创建Vector对象:Vector v=new Vector();
- 给向量添加元素:v.addElement(Object obj); //obj必须是对象
- 取出向量中的元素:Object obj=v.elementAt(0);
- 注意第一个元素的下标是0,返回值是Object类型的。
- 计算向量的长度:v.size();
- 若与最高分相差10分内:A等;20分内:B等;30分内:C等;其它:D等
4.2 包装类与基本数据类型的相互转换
1、装箱:基本数据类型→包装类型:具备引用类型变量的相关特征(封装、继承、多态),为了使用专门为对象设计的API和特性
法1:使用包装类型的构造器
法2:使用包装类中的valueOf(基本数据类型)
方法【推荐】,源代码定义的是静态方法,类/对象都能调用。性能更好!
总结:分析源码可知在包装类中定义的构造方法/valueOf()方法可以接受String类型的Number,并且对于Boolean是忽略大小写的不是true其余都是false。
Integer obj1 = new Integer(4);//使用构造函数函数
Float f = new Float(“4.56”);//可以是数值型的字符串类型
Float f = new Float(4.56f);
Long l = new Long(“asdf”); //NumberFormatException
Boolean bb1 = new Boolean("false");//"False123"也可识别的,分析源码可知:在忽略大小写的情况下只要和true不同就是false
//法2:使用包装类中的valueOf()方法
Integer obj2 = Integer.valueOf(4);//使用包装类中的valueOf方法
2、拆箱:包装类型→基本数据类型:为了能够进行运算
方法:调用包装类的xxxValue()方法,注意源码中这个方法不是静态的,只能通过包装类的对象调用!
源码:包装类中的xxxValue()方法
public int intValue(){
return value;
}
3、JDK5.0特性—自动拆箱、自动装箱:只能与自己对应的类型之间才能实现自动装箱与拆箱。
源码实现还是用的"包装类/对象.valueOf()"、“xxxValue(基本数据类型)”,通过查看字节码文件可以看到
包装类型==基本,会自动拆箱比较
4.3 String类型与包装类&基本类型的相互转换
1、包装类、基本类型----→String类型
调用String的重载的静态方法valueOf(xxx)
基本数据类型变量+“”,这样自动转
包装类中提供的静态方法valueOf()也有这个作用
2、String类型----→包装类、基本类型
String→基本:①包装类的构造器;②调用包装类静态方法的parse(基本类型)
String→包装类:包装类中的静态方法toString()方法
五、Record记录(暂时不看)
public record ZZZ() {
}
六、企业真题
1、接口与抽象类的区别?
抽象类属于类,单继承,必须依靠子类继承实例化才能调用其中的方法,可以含有抽象方法,可以没有
接口只含有抽象方法,多继承
2、接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承实现类(concrete class)?
是;是;是;
3、 接口可以有自己属性吗?
可以。必须是public static final的
4、访问接口的默认方法如何使用
使用实现类的对象进行调用。而且实现还可以重写此默认方法。
5、枚举可以继承吗?
使用enum定义的,其父类就是Enum类,就不要再继承其他的类了。
6、Java基本类型与包装类的区别