写点什么

java 之反射机制与国际化、网络编程、数据库编程、不可变对象

作者:andy
  • 2022-10-28
    北京
  • 本文字数:5973 字

    阅读完需:约 20 分钟

一、反射机制

当你了解了反射机制,就明白,这是一种逆向思维作用的结果,会感受到不同角度,在计算机语言中应用。同样,你也可以学习逆向思维,从 Java 语言中,学习到思维方位,再反向作用于你的人生。

Java 中的反射机制,大大促进了框架组件的发展,使得在框架应用上,比其他的语言都要多。

Java 开发中,正常的思维方式是先有了类,而后才能有对象。而“反”,就是指可以利用对象找到对象的出处,也就是对象所属的类。Object 提供的 Class 类就是专门存储对象所属类信息的这样一个专门的“类”,取得 Class 对象的方法可以通过 Object 提供的 getClass()方法。

public final Class getClass​()。

java.lang.Class 类是一切反射的源头。该类有三种实例化方式:

1、调用 Object 类中的 public final Class getClass​()方法,不常用;

2、使用“类.class”取得;

3、调用 Class 类中的 public static Class forName​(String className)throws ClassNotFoundException 方法。

对于 Class 类的作用,可以进行类对象的实例化。使用以下方法进行:

@Deprecated(since="9")

public T newInstance​()throws InstantiationException,IllegalAccessException

有了这一方法之后,从而获取对象所属类信息,进而进行对象实例化操作,可以不使用关键字 new 了。

但是会疑惑编写很多的代码,才能实现 new 的功能,比较麻烦。现实是 new 是造成耦合的最大元凶,一切的耦合都起源于 new。通过工厂设计模式加深理解。对于工厂设计模式,如果接口的子类不断增加,那么,就需要不断修改工厂类,使用关键字 new 实力化对象。对于工厂类,使用反射方式,就可以解决耦合问题,进行解耦合。

Class 类中实用的方法:

取得全部构造:public Constructor[] getConstructors​()throws SecurityException

取得制定参数的构造:public Constructor getConstructor​(Class... parameterTypes)throws NoSuchMethodException,SecurityException

以上方法返回的是 java.lang.reflect.Constructor 类,再通过 Constructor 类中的 newInstance()方法进行实例化。

public T newInstance​(Object... initargs)

throws InstantiationException,IllegalAccessException,

IllegalArgumentException,InvocationTargetException

通过以上方法,就会清楚,简单 Java 类中至少要保证有一个无参构造方法。实际上,Class 类中 newInstance()方法也是通过获取构造方法,再进行对象实例化。

取得类中的全部方法:public Method[] getMethods​()throws SecurityException

取得制定的方法:public Method getMethod​(String name,Class... parameterTypes)

throws NoSuchMethodException,SecurityException

以上方法返回 java.lang.reflect.Method 类,主要关注 invoke 方法。

public Object invoke​(Object obj,Object... args)

throws IllegalAccessException,IllegalArgumentException

,InvocationTargetException

取得类中的全部成员:public Field[] getDeclaredFields​()throws SecurityException

取得制定的成员:public Field getDeclaredField​(String name)

throws NoSuchFieldException,SecurityException

以上方法返回类型为 java.lang.reflect.Field 类,该类通过 get()取得属性内容,通过 set()方法设置属性内容。

public Object get​(Object obj)throws IllegalArgumentException,IllegalAccessException

public void set​(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException

Field 类的 java.lang.reflect.AccessibleObject 父类,可以通过设置私有属性的访问权限,使得类外部也可以直接访问私有属性,使用以下方法:

public void setAccessible(boolean flag)throws SecurityException

由此,可以得出,实际上,也不能保证完全的封装。

综合反射机制,对于 Java 类的实例化对象的方式,有一下三种

  • new;

  • clone;

  • 反射机制。


二、国际化

在进行程序开发时,核心代码不会因为语言环境的不同而变化,但是,对于资源文件内容而言,需要根据用户所在的语言环境显示对应的信息。在这一要求下,就出现了国际化的问题。国际化的第一个问题便是资源文件的读取。需要注意的是,在 Java 资源文件中,存储的是 Unicode 编码。

Java 提供了 java.util.ResourceBundle 类进行文件读取。

常用操作方法如下:

根据当前语言环境取得对象(baseName 为文件名,默认是 properties 文件类型):

public static final ResourceBundle getBundle​(String baseName)

指定语言环境取得对象:

public static final ResourceBundle getBundle​(String baseName,Locale locale)

简单取得内容:public final String getString​(String key)

java.text.MessageFormat 类提供了 public static String format​(String pattern,Object... arguments)方法,通过该方法可以设置内容。

有的时候需要灵活设置资源文件中的内容,可以使用占位符。示例如下:

{0},欢迎{1}光临,祝福您身体健康,阖家幸福!

为了实现国际化,资源文件需要不同的设置,如下:

中国:Message_zh_CN.properties

美国:Message_en_US.properties

注意:资源文件的名称,首字母必须大写。特定语言的资源文件读取的优先级低于公共语言资源文件读取的优先级。使用 ResourceBundle 读取 CLASSPATH 下的资源文件,不需要输入“.后缀名”。

从国际化这个概念,可以明确指定的内容显示,根据不同的地域显示不同的内容。但是,对于网页整体内容而言,不会进行全部的自动转换,而是,用户自行选择,然后,通过翻译组件实现页面内容的替换。


三、网络编程

1、简介

网络就是将各个终端连接到一起的,能够进行数据交互的空间。网络编程,则是服务器与客户端交互的编程开发。

实际的网络编程有两种:

C/S 结构(Client 与 Server):需要开发客户端代码和服务器端两套代码,非常麻烦。但是安全性高,使用内部定义的协议。Android 程序与服务器端程序的交互,有道云笔记也有 C/S 结构的应用。

B/S 结构(Browser 与 Server):只开发一套服务器端程序,利用浏览器进行数据交互。相比 C/S 结构,比较方便,但是安全性不高,使用公共的 HTTP 协议和公共的 80 端口。

目前所说的网络编程,指的是 C/S,需要开发两套程序,也就是使用 Socket 的程序。分为两类:

TCP 程序:采用可靠连接方式进行传输的程序;

UDP 程序:采用不可靠连接方式进行传输,属于数据报协议。

2、网络编程简易实现

网络编程开发主要使用到两个类:

服务器类:ServerSocket,工作在服务器端,用于接收客户的请求;

客户端类:Socket,工作在客户端,用于发送客户请求至服务器端。

ServerSocket 类常用方法:

构造方法(设置监听端口,防火墙也是针对端口):public ServerSocket​(int port)throws IOException

接收客户端连接:public Socket accept​()throws IOException

Socket 类常用方法:

取得客户端的输出信息:public OutputStream getOutputStream​()throws IOException

构造方法(包含服务器端的 IP 和端口):public Socket​(String host,int port)throws UnknownHostException,IOException

输入方法:public InputStream getInputStream​()throws IOException

服务器端代码示例:


public static void main(String[] args) throws Exception {		ServerSocket server = new ServerSocket(8888);		System.out.println("Waiting ......");		Socket client = server.accept();		PrintStream ps = new PrintStream(client.getOutputStream());		ps.println();		ps.close();		client.close();		server.close();	}
复制代码


这样执行程序,如果一直获得客户端的连接,则会一直等待下去,形成阻塞状态,影响性能,因此,使用 java.nio 包的相关类去解决。

客户端代码示例:


	public static void main(String[] args) throws Exception {		Socket client = new Socket("localhost", 8888);		Scanner scan = new Scanner(client.getInputStream());		System.out.println("Input:");		scan.useDelimiter("\n");		while(scan.hasNextLine()){			System.out.println(scan.nextLine());		}		scan.close();		client.close();	}
复制代码


3、Echo 模型

Echo 模型,客户端输入信息,将信息发送给服务器端,服务器端接收后,开头加上“ECHO:”标记返回。

设计思想:

采用每次输入的形式,每次连接后不立刻进行服务器关闭;

设置制定字符串,进行关闭服务器操作。

代码形式就不在这里展示。服务开发的基础要素:网络支持类、IO、多线程。

注意:要非常非常小心,输出和输入数据的结束判断,一旦判断不正确,会认为一直在进行传递,没有结束传递数据。

如以下代码的区别:

serverOutput.print("ECHO:" + input);

serverOutput.println("ECHO:" + input);


四、数据库编程


1、简介

Java 语言设计的时候除了考虑平台的技术,还提供了一些支持的服务,数据库便是其中支持的服务之一。这些服务最大的特点就是固定格式,流程固定,没有太多技术含量,属于应用。

JDBC(Java DataBase Connection),Java 数据库连接,Java 提供的一组与平台无关的数据库操作标准,结束数据库操作是,需要关闭数据库连接。

JDBC 技术范畴规定了四种 Java 数据库操作形式:

  • JDBC-ODBC 桥接技术(已不用)

ODBC 是开放数据库连接,微软提供的数据库连接应用,JDBC 间接使用 ODBC 操作数据库。

  • JDBC 直接连接

各个数据库开发商提供的驱动程序,Java 直接进行数据库操作,性能最好。

  • JDBC 网络连接

使用专门的数据库网络连接指令进行数据库操作,未来的主要方向。

  • 模拟指定数据库的通讯协议自己编写数据库操作


2、数据库连接

java.sql 是包含所有操作数据库的接口和类。主要类和接口如下:

一个类:DriverManager

四个接口:Connection、Statement、ResultSet、PreparedStatement

数据库操作流程:

  • 加载数据库的驱动程序(向容器加载)

  • 数据库连接(通过 DriverManager 取得 Connection 连接)

  • 进行数据的 CRUD 操作(通过 Statement、ResultSet、PreparedStatement 完成)

  • 关闭数据库资源

加载数据库驱动程序使用 Class.forName()进行。

数据库连接使用 java.sql.DriverManager 类的 public static Connection getConnection​(String url,String user,String password)throws SQLException 方法获取。每次数据库的连接都需要使用 Connection 接口进行连接。

数据库操作结束之后,数据库连接的关系使用 Connection 接口的 close()方法。一旦数据库连接关闭了,其他所有与数据库的操作都会无效。所以,进行数据库资源关闭时,就关闭这一个就好了。

提醒:只要涉及资源访问,都会抛出异常



图 数据库连接模型


由图可知,JDBC 进行数据库连接,采用的是工厂设计模式。DriverManager 就是一个工厂类,客户端调用时,会隐藏具体的实现子类。


3、数据库操作

数据库的操作可以使用 Statement 接口进行。Statement 接口的实例化对象通过 Connection 接口的 public Statement createStatement​()throws SQLException 方法取得。

Statement 常用方法:

更新操作(返回更新行数):int executeUpdate​(String sql)throws SQLException

查询操作:ResultSet executeQuery​(String sql)throws SQLException

对于数据查询而言,返回的结果需要使用 ResultSet 进行保存。因为返回的数据记录就只有几种数据类型,因此,ResultSet 是以数据类型进行每行数据的保存。也即每行记录为一个对象,记录的各个列的数据类型为属性。当然,也可以用 MyBatis 中的 VO 类来理解。



图 JDBC 查询结果处理原理


ResultSet 接口常用方法:

向下移动指针并判断是否有数据行(移动之后可以直接取得当前数据行中所有数据列的内容):

boolean next​()throws SQLException

取出列的内容:

Date getDate​(String columnLabel)throws SQLException

double getDouble​(String columnLabel)throws SQLException

int getInt​(String columnLabel)throws SQLException

String getString​(String columnLabel)throws SQLException

但是使用 Statement 接口有着极大的问题,例如 insert 语句时,输入的值带有单引号(Mr'Smith),那么,进行数据库操作时会报出错误。就是说明,Statement 在处理敏感字符的时候,束手无策。Java 中提供了 PreparedStatement 解决这样的问题。

PreparedStatement 实现完整的具备特殊标记符号的 SQL 语句。实例化对象通过 Connection 的 PreparedStatement prepareStatement​(String sql)throws SQLException 方法取得。但是,取得实例化对象的方法中,SQL 参数是一个具备特殊标记的完整 SQL 语句,但是没有内容。可以使用 PreparedStatement 中提供的一系列 setXxx()方法进行特殊标记设置内容。

更新操作:int executeUpdate​()throws SQLException

查询操作:ResultSet executeQuery​()throws SQLException

在进行 setDate()设置时,需要注意将 java.util.Date 转换为 java.sql.Date 或者相关的类。

java.util.Date 下的三个子类都在 java.sql 包下:

日期:java.sql.Date

时间:java.sql.Time

时间戳(日期时间):java.sql.Timestamp

需要将 java.util.Date 转换为 java.sql.Date 或者相关类需要依靠 long 类型。

java.util.Date 的 getTime()方法:将 Date 转换为 long

java.sql.Date 的 public Date​(long date)方法:将 long 转换为 Date

java.sql.Time 的 public Time​(long time)方法:将 long 转换为 Time

java.sql.Timestamp 的 public Timestamp​(long time)方法:将 long 转换为 Timestamp


4、批处理与事务处理

批处理就是一次向数据库中发出多条操作命令,一起执行。

Statement 接口:

增加批处理语句(但需要一条一条的添加):

void addBatch​(String sql)throws SQLException

执行批处理(数组表示每个 sql 执行后影响的行数):

int[] executeBatch​()throws SQLException

PreparedStatement 接口:

增加批处理语句:

void addBatch​()throws SQLException

执行批处理结果:

int[] executeBatch() throws SQLException

当进行批处理时,出现了其中的一条语句错误,造成之后的语句无法执行成功,又因为 JDBC 有自动提交的功能,会把出错语句前的执行成功的语句结果提交。这样是不好的。所以,JDBC 提供了事务处理机制。事务控制由 Connection 接口提供。

提交:void commit​()throws SQLException

回滚:void rollback​()throws SQLException

设置是否自动提交:void setAutoCommit​(boolean autoCommit)throws SQLException


五、不可变对象


不可变类

类的状态不可变,也就是说,类所生成的对象,其包含的属性内容不可更改。Java 中提供了 String、基本数据类型的包装类作为不可变类。

可变类

类的状态可以改变,所生成的对象,其包含的属性可以修改。

不可变类的设计

1、使用 final 定义类,不可变类不能被继承

2、成员变量由 final、private 修饰

3、不提供更改成员变量的 setter 方法

4、同构构造器初始化所有成员,进行深拷贝

5、getter 方法提供克隆对象的副本,不提供直接对象本身

常量池问题

String 以及基本数据类型的包装类,作为不可变对象的 Java 支持,通常一旦生成对象,即入常量池,除非通过关键字 new 生成新的对象无法入池,其他情况,则直接使用入池对象。


用户头像

andy

关注

还未添加个人签名 2019-11-21 加入

还未添加个人简介

评论

发布
暂无评论
java之反射机制与国际化、网络编程、数据库编程、不可变对象_andy_InfoQ写作社区