当前位置: 首页 > article >正文

Java 核心技术卷 I 学习记录六

Java 核心技术卷 I 学习记录六

    • 4、对象包装器与自动装箱
    • 5、参数数量可变的方法
    • 6、枚举类
    • 7、反射
      • 1、Class 类
      • 2、捕获异常
      • 3、利用反射分析类的能力
      • 4、在运行时使用反射分析对象
      • 5、使用反射编写泛型数组代码
      • 6、调用任意方法
    • 8、继承的设计技巧

4、对象包装器与自动装箱

所有的基本类型都冇一个与之对应的类。例如,Integer类对应基本类型int。通常,这些类称为包装器(wrapper)这些对象包装器类拥有很明显的名字:IntegerLongFloatDoubleShortByteCharacterVoidBoolean(前6个类派生于公共的超类Number)。对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,对象包装器类还是final,因此不能定义它们的子类。
不允许写成ArrayList<int>。这里就用到了Integer对象包装器类。我们可以声明一个Integer对象的数组列表。

ArrayList<Integer> list = new ArrayList<>();

有一个很有用的特性,从而更加便于添加int类型的元素到ArrayList<Integer>中。

// 调用
list.add(3);
// 自动地变换成
list.add(Integer.valueOf(3));
// 这种变换叫做自动装箱(autoboxing)

当将一个Integer对象赋给一个int值时,将会自动拆箱。

// 编译器将下语句
int n = list.get();
// 翻译成:
int n = list.get(i).intValue();

甚至在算术表达式中也能够自动装箱和拆箱。

// 将自增操作符应用于一个包装器引用:
Integer n = 3;
n++;
// 编译器自动插入一条对象拆箱的命令,然后进行自增运算,再将结果装箱

大多数情况下,容易有一种假象,即基本类型与它们的对象包装器是一样的,只是它们的相等性不同。大家知道,==运算符也可以应用于对象包装器对象,只不过检测的是对象是否指向同一个存储区域,因此,下面的比较通常不会成立:

Integer a = 1000;
Integer b = 1000;
if (a = b) ...

但是,Java却可能让他成立。
如果将经常出现的值包装到一个对象中,就有可能成立。要获得确定结果,在两个包装器对象比较时调用equals方法。

// 首先,包装类引用可以为null,所以自动装箱可能会抛出一个NullPointerException异常
Integer n = null;
System.out.println(2 * n);  // Throws NullPointerException

// 如果在一个条件表达式中混合使用Integer和Double类型,Integer值就会拆箱,提示为double,再装箱为Double:
Integer n = 1;
Double x = 2.0;
System.out.println(true ? n : x);  // Prints 1.0

装箱和拆箱是编译器认可的,而不是虚拟机。编译器在生成类的字节码时,插人必要的方法调用。虚拟机只是执行这些字节码。
使用数值对象包装器还有另外一个好处

// 将字符串转换为整型,可以将基本方法放置在包装器中
int x = Integer.parseInt(s);
// parseInt是一个静态方法,与Integer对象没有关系

java.lang.Integer 1.0

  • int intValue():以int的形式返回Integer对象的值(在Number类中覆盖了intValue方法)。
  • static String toString(int i):以一个新String对象的形式返回给定数值i的十进制表示。
  • static String toString(int i ,int radix):返回数值i的基于给定radix参数进制的表示。
  • static int parselnt(String s):返回字符串s表示的整型数值,给定字符串表示的是十进制的整数。
  • static int parseInt(String s,int radix):返回字符串s表示的整型数值,给定字符串表示的是radix参数进制的整数。
  • static Integer valueOf(String s):返回用s表示的整型数值进行初始化后的一个新Integer对象,给定字符串表示的是十进制的整数。
  • Static Integer value Of(String s, int radix):返回用s表示的整型数值进行初始化后的一个新Integer对象,给定字符串表示的是radix参数进制的整数。

java.text.NumberFormat 1.1

  • Number parse(String s):返回数字值,假设给定的String表示了一个数值。

5、参数数量可变的方法

在Java SE 5.0以前的版本中,每个Java方法都有固定数量的参数。然而,现在的版本提供了可以用可变的参数数量调用的方法(有时称为“变参”方法)。

// 调用printf方法 
// 包含两个参数的调用
System.out.printf("%d", n);
// 包含三个参数的调用
System.out.printf("%d %s", n, "widgets");

printf方法的定义

public class PrintStream {
	public PrintStream printf(String fmt , Object... args) { 
		return format(fmt, args); 
	} 
}

这里的省略号...是Java代码的一部分,表明这个方法可以接收任意数量的对象(除fmt参数之外)。
printf接收两个参数,一个是格式字符串,另一个是Object[]数组,保存所有的参数(如果调用者提供的是整型数组或其他基本类型的值,直接装箱把它们转换成对象)。根据fmt字符串,将第i个格式说明符与args[i]的值匹配起来。
也可以说,对于printfObject...参数类型与Object[]完全一样。

6、枚举类

枚举类的典型例子如下:

// 声明定义的类型是一个类,刚好又4个实例
public enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };

在比较两个枚举类型的值时,不需要使用equals,直接使用"=="就可以。
需要的话也可以在枚举类型中添加一些构造器、方法和域。
构造器只是在构造枚举常量的时候被调用。

public enum Size {
	SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
	private String abbreviation;
	private Size(String abbreviation) { this.abbreviation = abbreviation; }
	public String getAbbreviation() { return abbreviation; }
}

所有的枚举类型都是Enum类型的子类。它们继承了这个类的许多方法。
toString方法是最有用的一个方法,能够返回枚举常量名。

Size.SMALL.toString();   // "SMALL"

toString方法的逆方法是静态方法valueOf

// 将s设置成Size.SMALL
Size s = Enum.valueOf(Size.class, "SMALL");

每个枚举类型都有一个静态方法values,将返回一个包含全部枚举值的数组。

Size[] values = Size.values();
// [Size.SMALL, Size.MEDIUM, Size.LARGE, Size.EXTRA_LARGE]

oridinal方法返回enum声明中枚举常量的位置,从0开始计数。

Size.MEDIUM.ordinal();   // 1

java.Iang.Enum 5.0

  • static Enum valueOf(Class enumClass , String name):返回指定名字、给定类的枚举常量。
  • String toString():返回枚举常量名。
  • int ordinal ():返回枚举常量在enum声明中的位置,位置从0开始计数。
  • int compareTo(E other):如果枚举常量出现在Other之前,则返回一个负值;如果this=other,则返回0;否则,返回正值。枚举常量的出现次序在enum声明中给出。

7、反射

能够分析类能力的程序称为反射(reflective)。反射机制的功能极其强大。在下面可以看到,反射机制可以用来:

  • 在运行时分析类的能力。
  • 在运行时查看对象,例如,编写一个toString方法供所有类使用。
  • 实现通用的数组操作代码。
  • 利用Method对象, 这个对象很像中的函数指针。

反射是一种功能强大且复杂的机制。使用它的主要人员是工具构造者,而不是应用程序员。

1、Class 类

在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。
可以通过专门的Java类访问这些信息。保存这些信息的类被称为Class,这个名字很容易让人混淆。Object类中的getClass()方法将会返回一个Class类型的实例。

Employee e;
...
Class cl = e.getClass();

如同用一个Employee对象表示一个特定的雇员属性一样,一个Class对象将表示一个特定类的属性。
最常用的Class方法是getName。这个方法将返回类的名字。

System.out.println(e.getClass().getName() + " " + e.getName());  // Employee Harry Hacker

如果类在一个包里,包的名字也作为类名的一部分:

Random generator = new Random();
Class cl = generator.getClass();
String name = cl.getName(); // name is set to "java.util.Random"

// 还可以调用静态方法forName获得类名对应的Class对象
String dassName = "java.util .Random";
Class cl = Class.forName(dassName);

如果类名保存在字符串中,并可在运行中改变,就可以使用这个方法。当然,这个方法只有在dassName是类名或接口名时才能够执行。否则,forName方法将抛出一个checked exception(已检查异常)。无论何时使用这个方法,都应该提供一个异常处理器( exception handler)。
获得Class类对象的第三种方法非常简单。如果T是任意的Java类型(或void关键字),T.class将代表匹配的类对象。

Class dl = Random.class; // if you import java.util
Gass cl2 = int.class;
Class cl3 = Double[].class;

请注意,一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。
虚拟机为每个类型管理一个Class对象。因此,可以利用==运算符实现两个类对象比较的操作;还有一个很有用的方法newlnstance(),可以用来动态地创建一个类的实例。

// 利用==运算符实现两个类对象比较的操作
if (e.getClass() == Employee.class) ...
// 方法newlnstance(),可以用来动态地创建一个类的实例
e.getClass().newlnstance();

2、捕获异常

当程序运行过程中发生错误时,就会“抛出异常“。抛出异常比终止程序要灵活得多,这是因为可以提供一个“捕获”异常的处理器(handler)对异常情况进行处理。
如果没有提供处理器,程序就会终止,并在控制台上打印出一条信息,其中给出了异常的类型。
异常有两种类型:未检查异常和已检查异常。对于已检查异常,编译器将会检查是否提供了处理器。然而,有很多常见的异常。例如,访问null引用,都属于未检查异常。编译器不会査看是否为这些错误提供了处理器。
将可能抛出已检査异常的一个或多个方法调用代码放在try块中,然后在catch子句中提供处理器代码。

try {
	statements that might throwexceptions
}
catch (Exception e) {
	handler action
}

如果try块中没有抛出任何异常,那么会跳过catch子句的处理器代码。
java.lang.Class 1.0

  • static Class forName(String className):返回描述类名为 classNameClass对象。
  • Object newlnstance():返回这个类的一个新实例。

java.Iang.reflect.Constructor 1.1

  • Object newlnstance(Object[] args):构造一个这个构造器所属类的新实例。
    参数:args这是提供给构造器的参数。

java.Iang.Throwable 1.0

  • void printStackTrace():将Throwable对象和栈的轨迹输出到标准错误流。

3、利用反射分析类的能力

在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。这三个类都有一个叫做getName的方法,用来返回项目的名称。
Field类有一个getType方法,用来返回描述域所属类型的Class对象。
Method和Constructor类有能够报告参数类型的方法,Method类还有一个可以报告返回类型的方法。这个类还有一个叫做getModifiers的方法,它将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用状况。
另外,还可以利java.lang.reflect包中的Modifier类的静态方法分析getModifiers返回的整型数值。
Class类中的getFields、getMethods和getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类的getDeclareFields、getDeclareMethods和getDeclaredConstructors方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。
java.lang.Class 1.0

  • Field[] getFields() 1.1:getFields方法将返回一个包含Field对象的数组,这些对象记录了这个类或其超类的公有域。
  • Filed[] getDeclaredFields() 1.1:getDeclaredField方法也将返回包含Field对象的数组,这些对象记录了这个类的全部域。如果类中没有域,或者Class对象描述的是基本类型或数组类型,这些方法将返回一个长度为0的数组。
  • Method[] getMethods() 1.1:返回包含Method对象的数组:getMethods将返回所有的公有方法,包括从超类继承来的公有方法。
  • Method[] getDeclareMethods() 1.1:返回包含Method对象的数组:getDeclaredMethods返回这个类或接口的全部方法,但不包括由超类继承了的方法。
  • Constructor[] getConstructors() 1.1:返回包含Constructor对象的数组,其中包含了Class对象所描述的类的所有公有构造器(getConstructors)。
  • Constructor[] getDeclaredConstructors() 1.1:返回包含Constructor对象的数组,其中包含了Class对象所描述的类的所有构造器(getDeclaredConstructors)。

java.lang.reflect.Field 1.1
java.lang.reflect.Method 1.1
java.lang.reflect.Constructor 1.1

  • Class getDeclaringClass():返冋一个用于描述类中定义的构造器、方法或域的Class对象。
  • Class[] getExceptionTypes():(在Constructor和Method类中)返回一个用于描述方法抛出的异常类型的Class对象数组。
  • int getModifiers():返回一个用于描述构造器、方法或域的修饰符的整型数值。使用Modifier类中的这个方法可以分析这个返回值。
  • String getName():返冋一个用于描述构造器、方法或域名的字符串。
  • Class[] getParameterTypes():(在Constructor和Method类中)返回一个用于描述参数类型的Class对象数组。
  • Class getReturnType() :(在Method类中)返回一个用于描述返回类型的Class对象。

java.lang.reflect.Modifier 1.1

  • static String toString(int modifiers):返回对应modifiers中位设置的修饰符的字符串表示。
  • static boolean isAbstract(int modifiers)
  • static boolean isFinal (int modifiers)
  • static boolean islnterface(int modifiers)
  • static boolean isNative(int modifiers)
  • static boolean isPrivate(int modifiers)
  • static boolean isProtected(int modifiers)
  • static boolean isPublic(int modifiers)
  • static boolean isStatic(int modifiers)
  • static boolean isStrict(int modifiers)
  • static boolean isSynchronized(int modifiers)
  • static boolean isVolatile(int modifiers)
    这些方法将检测方法名中对应的修饰符在modffiers值中的位。

4、在运行时使用反射分析对象

已经知道如何查看任意对象的数据域名称和类型:

  • 获得对应的Class对象。
  • 通过Class对象调用getDeclaredFields。

java.Iang.reflect.AccessibleObject 1.2

  • void setAccessible(boolean flag):为反射对象设置可访问标志。flag为true表明屏蔽Java语言的访问检查,使得对象的私有属性也可以被査询和设置。
  • boolean isAccessible():返回反射对象的可访问标志的值。
  • static void setAccessible(AccessibleObject[] array,boolean flag):是一种设置对象数组可访问标志的快捷方法。

java.lang.Class 1.1

  • Field getField(String name):返回指定名称的公有域。
  • Field[] getField():返回包含所有域的数组。
  • Field getDeclaredField(String name):返回类中声明的给定名称的域。
  • Field[] getDeclaredFields():返回包含声明的全部域的数组。

java.Iang.reflect.Field 1.1

  • Object get(Object obj):返回obj对象中用Field对象表示的域值。
  • void set(Object obj ,Object newValue):用一个新值设置Obj对象中Field对象表示的域。

5、使用反射编写泛型数组代码

java.lang.reflect包中的Array类允许动态地创建数组。
java.lang.reflect.Array 1.1

  • static Object get(Object array,int index):
  • static xxx getXxx(Object array,int index):(xxx是boolean、byte、char、double、float、int、long、short之中的一种基本类M)。这些方法将返回存储在给定位置上的给定数组的内容。
  • static void set(Object array,int index,Object newValue):
  • static setXxx(Object array,int index,xxx newValue):(xxx是boolean、byte、char、double、float、int、long、short之中的一种基本类型)。这些方法将一个新值存储到给定位置上的给定数组中。
  • static int getLength(Object array):返回数组的长度。
  • static Object newInstance(Class componentType,int length):
  • static Object newInstance(Class componentType,int[]lengths):返回一个具有给定类型、给定维数的新数组。

6、调用任意方法

从表面上看,Java没有提供方法指针,即将一个方法的存储地址传给另外一个方法,以便第二个方法能够随后调用它。事实上,Java的设计者曾说过:方法指针是很危险的,并且常常会带来隐患。他们认为Java提供的接口(interface)是一种更好的解决方案。然而,反射机制允许你调用任意方法。
java.Iang.reflect.Method 1.1

  • public Object invoke(Object implicitParameter,Object[] explicitParamenters):调用这个对象所描述的方法,传递给定参数,并返回方法的返回值。对于静态方法,把null作为隐式参数传递。在使用包装器传递基本类型的值时,基本类型的返回值必
    须是未包装的。

8、继承的设计技巧

对设计继承关系很有帮助的建议。

    1. 将公共操作和域放在超类
    1. 不要使用受保护的域
    1. 使用继承实现“ is-a” 关系
    1. 除非所有继承的方法都有意义,否则不要使用继承
    1. 在覆盖方法时,不要改变预期的行为
    1. 使用多态, 而非类型信息
    1. 不要过多地使用反射

http://www.kler.cn/a/397756.html

相关文章:

  • 嵌入式课程day13-C语言指针
  • 超全超详细使用SAM进行高效图像分割标注(GPU加速推理)
  • 常见的测试方法
  • 车载诊断架构 --- 关于DTC的开始检测条件
  • nginx源码安装配置ssl域名
  • Flutter:Widget生命周期
  • Scala中的Array
  • 基于树莓派的边缘端 AI 目标检测、目标跟踪、姿态估计 视频分析推理 加速方案:Hailo with ultralytics YOLOv8 YOLOv11
  • 鸿蒙生态的未来:共融与创新
  • HbuilderX 插件开发-模板创建
  • 将自定义函数添加到MATLAB搜索路径的方法
  • 【视觉SLAM】1-概述
  • java笔试练习题笔记(9)
  • GRE做题笔记(零散的个人经验)
  • 云渲染与云电脑,应用场景与技术特点全对比
  • 【RabbitMQ】10-抽取MQ工具
  • 高性能linux服务器运维实战 shell应用案例
  • C# yolo10使用onnx推理
  • SpringBoot整合Email 邮件发送详解
  • Java基础——多线程
  • SOP搭建:企业标准化操作程序构建与实施指南
  • 用com.github.shyiko.mysql.binlog 写一个监听mysql的binlog 的程序
  • 【代码随想录day32】【C++复健】509. 斐波那契数;70. 爬楼梯;746. 使用最小花费爬楼梯
  • Java-01 深入浅出 MyBatis - MyBatis 概念 ORM映射关系 常见ORM 详细发展历史
  • [刷题]入门1.矩阵转置
  • 单片机_day7_中断