前言
通过Java基础代码示例,与朋友们分享Java语法的精髓,希望通过本篇博客帮助大家学习
Java 反射 getClass()
Java 的反射机制允许在运行时检查和操作类的属性和方法。
getClass() 方法返回对象的 Class 对象,该对象包含有关对象实际类型的信息。
例如:
public class Person {
private String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
我们可以这样使用 getClass():
Person p = new Person("John", 30);
// 获取 Person 类的 Class 对象
Class c = p.getClass();
// 获取类名
String name = c.getName(); // "Person"
// 获取私有字段
Field f = c.getDeclaredField("name");
// 访问私有字段(需要设置可访问性)
f.setAccessible(true);
f.get(p); // "John"
// 调用公共方法
Method m = c.getMethod("toString");
m.invoke(p); // Person@7f31245a
所以 getClass() 允许我们在运行时探查对象的类型信息,并访问它的字段和方法。这在 Java 反射机制中起着非常重要的作用。
利用Java反射实现两个具有相同属性bean赋值
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public void copyProperties(Object src, Object dest) throws Exception {
Class srcClass = src.getClass();
Class destClass = dest.getClass();
// 获取所有字段
Field[] srcFields = srcClass.getDeclaredFields();
Field[] destFields = destClass.getDeclaredFields();
// 遍历字段,根据名称匹配并赋值
for (Field srcField : srcFields) {
for (Field destField : destFields) {
if (srcField.getName().equals(destField.getName())) {
// 设置私有字段可访问
srcField.setAccessible(true);
destField.setAccessible(true);
// 获得字段值并赋值
Object value = srcField.get(src);
destField.set(dest, value);
}
}
}
}
public static void main(String[] args) throws Exception {
Person p1 = new Person("John", 30);
Person p2 = new Person("Tom", 20);
copyProperties(p1, p2);
System.out.println(p2.name); // John
System.out.println(p2.age); // 30
}
new BeanHandler<Student>(Student.class)
BeanHandler 是一个 JDBC ResultSetHandler,它可以将 ResultSet 的每一行数据映射为一个 JavaBean 实例。
例如,如果有一个 Student 类:
public class Student {
private int id;
private String name;
private int age;
// getter/setter 省略
}
以及一张 student 表:
我们可以这样从 ResultSet 获取 Student 对象列表:
List<Student> students = jdbcTemplate.query(
"select * from student",
new BeanHandler<Student>(Student.class)
);
这会映射 ResultSet 的每一行到一个 Student 对象,所以 students 会是:
[Student{id=1, name=John, age=30}, Student{id=2, name=Mary, age=20}]
BeanHandler 接受的类 Class 对象定义了 JavaBean 的结构,所以它知道 ResultSet 中的哪些列映射到 bean 的哪些属性。
这使得从数据库结果集转换为 Java 对象 becomes 非常简单方便。
BeanHandler 是 JDBC 的 ResultSetHandler 接口的一种实现,Spring 在 JdbcTemplate 中提供了这种方便的映射功能。
总结:
new BeanHandler
- BeanHandler 是一个 ResultSetHandler 实现
- 它将 ResultSet 的每一行映射为指定的 JavaBean 对象
- 这里是创建一个 BeanHandler,将行映射为 Student 对象
- 所以它可以方便地将查询结果集转换为 Student 对象列表
Java是动态语言吗?JavaCompiler实现动态编译,并通过反射赋值
Java 不是一门完全动态的语言。它是一门静态语言,但也具有一定的动态特性。
Java 的动态特性主要体现在:- 反射:允许在运行时检查和操作类的属性和方法。这使得 Java 具有一定的动态特性。
- 动态代理:在运行时生成代理类和方法。
- 动态编译:可以在运行时编译 Java 源代码并执行。
例如,可以这样动态编译和执行 Java 代码:String code = "public class Hello { public static void main(String[] args) { System.out.println('Hello'); } }"; // 获取动态编译器 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 编译源代码 int compileResult = compiler.run(null, null, null, code.getBytes()); // 加载并执行主类 if(compileResult == 0) { URLClassLoader loader = URLClassLoader.newInstance(new URL[] { new File(".").toURI().toURL() }); Class clazz = loader.loadClass("Hello"); Method main = clazz.getMethod("main", String[].class); main.invoke(null, new Object[] { null }); }
这会输出:
Hello
所以通过 JavaCompiler 和反射,Java 程序在运行时可以动态编译和执行 Java 代码。这就是 Java 的动态特性的具体体现。
但是 Java 仍然是一个静态类型的语言,它在编译时需要确定变量和方法的参数的具体类型。这与动态语言不同,动态语言可以在运行时再确定变量的类型。
所以总结来说,Java 不是一门完全动态的语言,但它具有一定的动态特性,比如反射、动态代理和动态编译等。这给予 Java 一定的动态灵活性,但它仍保留了静态语言的诸多优点。HashMap转JavaBean
HashMap 可以通过反射机制转换为 JavaBean 对象。例如,有一个 HashMap 如下:
HashMap<String, Object> map = new HashMap<>(); map.put("name", "John"); map.put("age", 30); map.put("city", "New York");
我们可以这样转换为 JavaBean:
public class Person { private String name; private int age; private String city; // getter/setter 省略 } Person person = convertMapToBean(map, Person.class);
convertMapToBean() 方法如下:
public <T> T convertMapToBean(Map<String, Object> map, Class<T> beanClass) { try { T bean = beanClass.newInstance(); // 获得bean的所有属性 Field[] fields = beanClass.getDeclaredFields(); // 遍历map for (Map.Entry<String, Object> entry : map.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); // 在bean属性中找到与key匹配的属性 Field field = null; for (Field f : fields) { if (f.getName().equals(key)) { field = f; break; } } // 为bean属性赋值 if (field != null) { field.setAccessible(true); field.set(bean, value); } } return bean; } catch (Exception e) { e.printStackTrace(); return null; } }
这个方法通过反射获得 beanClass(这里是 Person)的所有属性,然后遍历 map,找到 key 与属性名匹配的属性,使用属性的 setter 方法为其赋值。
所以最后 person 会是:
Person{name=John, age=30, city=New York}
这就是如何使用反射机制将 HashMap 转换为 JavaBean 对象的实现方法。FileUtils、StringUtil、CollectionUtils、ArrayUtils
这些都是Apache Commons Lang包中的一些实用工具类。
- FileUtils:文件工具类,提供文件名过滤、文件类型判断、文件复制等方法。
- StringUtils:字符串工具类,提供字符串判断、替换、分割、格式化等方法。
- CollectionUtils:集合工具类,提供集合判断、比较、操作等方法。
- ArrayUtils:数组工具类,提供数组判断、加入、去重、截取等方法。
一些示例:
FileUtilsString extension = FileUtils.getExtension("somefile.txt"); // txt boolean isPdf = FileUtils.isFileExtension("somefile.pdf", "pdf"); // true
StringUtils
boolean isEmpty = StringUtils.isEmpty(null); // true boolean isAlpha = StringUtils.isAlpha("abc123"); // false String join = StringUtils.join(["a", "b", "c"], "-"); // a-b-c
CollectionUtils
Collection<String> list1 = Arrays.asList("a", "b", "c"); Collection<String> list2 = Arrays.asList("a", "b", "c"); boolean isEqual = CollectionUtils.isEqualCollection(list1, list2); // true
ArrayUtils
String[] withNull = {"a", null, "b"}; String[] withoutNull = ArrayUtils.removeElement(withNull, null); // ["a", "b"]
这些工具类提供了很多实用的方法,可以避免自己重复编写类似的方法,并且具有更高的稳定性和鲁棒性。所以在实际开发中经常会用到这些工具类。
Commons Lang还包含其他实用工具类,如BeanUtils、ClassUtils等,涵盖字符串、集合、文件、反射等方方面面,是Java开发者的好帮手。Java通过Jsoup解析HTML文件
Jsoup 是一个Java的HTML解析器,它可以很方便地从HTML内容或文件中提取数据。
例如,有一个test.html文件:<html> <head> <title>Test HTML</title> </head> <body> <p id="p1">Hello</p> <p id="p2">World</p> </body> </html>
我们可以这样使用Jsoup来解析这个HTML文件:
// 加载HTML文件 File input = new File("test.html"); Document doc = Jsoup.parse(input, "UTF-8"); // 获取HTML标题 String title = doc.title(); // Test HTML // 获取所有的p标签 Elements ps = doc.select("p"); // 获取id为p1的p标签内容 String p1Content = ps.get(0).text(); // Hello // 获取所有a标签的href属性 Elements links = doc.select("a[href]"); // 获取类为external的第一个a标签 Element link = doc.select("a.external").first(); // 设置id为p1的内容为"Hello World" doc.select("#p1").first().text("Hello World"); // 将修改后的HTML内容打印出来 String html = doc.html(); System.out.println(html);
这些只是Jsoup的一小部分功能,它可以方便地:
- 加载HTML文件/内容
- 通过标签选择器提取数据
- 操作HTML元素和属性
- 输出修改后的HTML
所以Jsoup是一个功能强大的HTML解析器,可以在Java中方便的实现对HTML的解析与处理。在实际开发中,我们常常需要从HTML网页中抓取数据,Jsoup就是一个好的选择。Java通过QRCode生成二维码
首先,添加Maven依赖:
<dependency> <groupId>com.github.ziutek</groupId> <artifactId>QRCode</artifactId> <version>2.2</version> </dependency>
然后,可以这样生成二维码:
// 生成二维码内容 String text = "http://www.example.com"; // 设置二维码图片参数 Hashtable<EncodeHintType, Object> hints = new Hashtable<>(); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); hints.put(EncodeHintType.MARGIN, 1); // 生成二维码 BitMatrix matrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, 200, 200, hints); // 创建图片格式化器 MatrixToImageWriter.writeToFile(matrix, "png", new File("qrcode.png"));
这会生成一个 200x200 像素的QR码图片qrcode.png,内容为"http://www.example.com"。
我们可以设置不同的参数来控制二维码的样子: - ERROR_CORRECTION:错误校正级别,值越高则可恢复的错误信息越多
- MARGIN:二维码边框大小,值越小则二维码网格密度越大
- width/height:二维码图片尺寸
然后通过MatrixToImageWriter可以很方便地将Matrix转换为不同的图片格式,如PNG、JPG等。
zuobar/QRCode这个库实现了二维码的编码和解码,是一个生成二维码的简单易用的Java库。使用它我们可以在Java中非常方便地生成和解析二维码图片。
二维码在实际生活中有很多应用,如商品识别、小程序登录、信息交换等场景。所以掌握Java生成和识别二维码的方法还是很有用的。Java通过Process执行C# exe程序
Java可以通过ProcessBuilder执行外部程序,包括C#编写的exe程序。
例如,我们有一个C# Console程序,代码如下:using System; namespace ConsoleApp { class Program { static void Main(string[] args) { Console.WriteLine("Hello from C#!"); } } }
编译后得到ConsoleApp.exe可执行文件。
我们可以在Java中这样执行它:ProcessBuilder builder = new ProcessBuilder("ConsoleApp.exe"); Process process = builder.start(); // 等待程序运行结束 process.waitFor(); // 获取程序输出 InputStream is = process.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } // 获取程序返回值 int exitValue = process.exitValue();
这会输出:
Hello from C#!
并且exitValue会是0,表示正常结束。
所以通过Process和ProcessBuilder,我们可以在Java中启动和控制C#编写的exe程序,获得它的输出和返回值。这为Java与C#程序之间的交互提供了一种简单的方法。
当然,这同样适用于Java启动和控制任何外部程序,不仅仅是C#程序。
只需要传入外部程序的可执行文件的路径就可以。ProcessBuilder还支持设置环境变量、工作目录、stdin和stdout管道等,是启动外部进程的强大工具。
所以掌握Java如何启动和控制外部进程,对于日常的开发工作来说还是很有用的。
特别是在需要与其他语言编写的程序交互时,这一技能就派上用场了。Google核心库GUAVA
Guava是Google开发的Java核心库,提供了许多实用的工具类和集合类型。
它包含的内容很丰富,这里介绍几个常用的模块:集合工具
提供了强大的集合工具类,如join()、split()、filtering、transforming等,例如:
List<String> list = Arrays.asList("a", "b", "c"); String join = Joiner.on("-").join(list); // a-b-c
提供的集合类型有:
- Multiset:提供set语义但每个元素可以出现多次
- BiMap:一个value可以映射到多个key
- Table:row key到column key的映射
缓存
提供基于弱引用和软引用的缓存实现,如:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() .maximumSize(100) .build( new CacheLoader<Key, Graph>() { public Graph load(Key key) throws AnyException { return createExpensiveGraph(key); } } );
函数式风格
提供了Lambda表达式相关的工具,如Functions、Predicates等,例如:
Predicate<String> predicate = Predicates.contains("hello"); predicate.apply("hello world"); // true
常见工具
提供字符串、基本类型、反射、I/O、HashCode等工具:
- Strings:join()、split()、nullToEmpty()等
- Primitives:返回基本类型的wrapper类型等
- Reflection:返回Class对象的所有的方法等
- IO:read()、write()、toByteArray()等
- HashCodes:计算对象的hashcode
Guava提供的工具类和集合类型可以大大提高开发效率,减少重复代码的编写。它被广泛应用于Java程序的开发中,是每一个Java开发者都应该掌握的强大工具库。利用百度语音识别技术实现文字转语音的应用
百度语音识别提供了文字转语音的技术能力,我们可以利用它来实现一个简单的文字转语音的应用。
主要步骤是:- 注册百度语音开发者账号,获取API Key和Secret Key
- 发送HTTP请求到百度语音合成服务,传入待合成的文本
- 获取返回的音频二进制数据
- 播放或者下载该音频
关键代码如下:// 发起HTTP请求 String url = "http://tsn.baidu.com/text2audio"; Map<String, String> params = new HashMap<>(); params.put("tex", text); // 合成文本 params.put("lan", "zh"); // 语言 params.put("cuid", "my_id"); // 用来区分用户 params.put("ctp", "1"); // 客户端类型:1为语音合成服务器 params.put("tok", accessToken); // 鉴权token String response = HttpUtil.post(url, params); // 解析返回的音频二进制数据 JSONObject json = JSONObject.parseObject(response); String speech = json.getString("speech"); String speechParam = json.getString("speech_param"); byte[] audio = Base64.decode(speech); // 播放返回的音频数据 AudioInputStream ais = AudioSystem.getAudioInputStream(new ByteArrayInputStream(audio)); AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File("speech.wav"));
这个简单例子向百度语音合成服务发送文本“你好”,获取返回的音频数据,并保存到speech.wav文件中。
百度语音识别提供的文字转语音功能支持设置语速、音调和音量,可以实现比较自然的语音合成效果。利用这个API,我们可以开发出各种语音应用,如文字朗读器、在线电台等。本例子介绍的技术也可以应用于聊天机器人的语音回复等功能的实现。
所以了解如何利用百度语音识别API来实现语音合成,是对语音领域比较有兴趣的Java开发者比较有价值的。Java Math函数简介
Java中的Math类提供了许多常用的数学函数。这里简单介绍几个常用的函数:
绝对值和取整
Math.abs(x); // 绝对值,x可以是int、double等 Math.ceil(x); // 向上取整 Math.floor(x); // 向下取整 Math.round(x); // 四舍五入
例如:
Math.abs(-5); // 5 Math.ceil(3.1); // 4 Math.floor(3.9); // 3 Math.round(3.5); // 4
最大/最小值
Math.max(x, y); // 返回x和y中的最大值 Math.min(x, y); // 返回x和y中的最小值
可以传入多个参数,返回其中最大/最小的值。
随机数
Math.random(); // 返回0.0到1.0的随机double值 Math.round(Math.random()); // 返回0到1的随机整数 Math.round(Math.random() * n); // 返回0到n-1的随机整数
三角函数
Math类提供所有基本的三角函数方法:
Math.sin(x); Math.cos(x); Math.tan(x); Math.asin(x); Math.acos(x); Math.atan(x); Math.atan2(y, x);
开方和指数
Math.sqrt(x); // 开平方根√x Math.pow(x, y); // x的y次幂 x^y Math.exp(x); // e的x次幂 Math.log(x); // 自然对数log_e(x) Math.log10(x); // 以10为底的对数log_10(x)
Math类提供的这些方法可以满足我们在日常编程中大部分的数学计算需求。除此之外,Math还包含一些除法余数、自增函数等方法。
所以熟悉Java的Math类,对于任何Java程序员来说都是非常有必要的。这有助于在代码中更加优雅和高效地实现各种数学运算。Java操作pdf的工具类itext
iText是一套开源的PDF库,可以用于PDF文件的创建、操作和检索。使用iText,我们可以在Java中实现对PDF文件的各种操作。
下面介绍一些使用iText的示例:创建PDF文件
// 新建Document和PDFWriter Document document = new Document(); PdfWriter.getInstance(document, new FileOutputStream("test.pdf")); // 打开文档 document.open(); // 添加段落 document.add(new Paragraph("Hello World!")); // 添加列表 List list = new List(); list.add(new ListItem("Item 1")); list.add(new ListItem("Item 2")); document.add(list); // 添加图片 Image img = Image.getInstance("image.png"); document.add(img); // 关闭文档 document.close();
这会生成一个包含文本、列表和图片的简单PDF文件。
读取和解析PDF文件
// 加载PDF文件 PdfReader pdfReader = new PdfReader("test.pdf"); // 获取页面数量 int n = pdfReader.getNumberOfPages(); // 读取每一页 for (int i = 0; i < n; i++) { PdfDictionary page = pdfReader.getPageN(i + 1); // 获取页中的元素 PdfArray contents = page.getAsArray(PdfName.CONTENTS); } // 获取文档信息 PdfDocument pdf = new PdfDocument(pdfReader); PdfDictionary info = pdf.getTrailer().getAsDictionary(PdfName.INFO);
可以读取PDF文件的元信息、页面数量、每个页面中的元素等。
修改现有PDF文件
// 读取PDF文件 PdfReader reader = new PdfReader("test.pdf"); // 新建PDF文件用于修改后的内容 PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("test_new.pdf")); // 获取第3页 PdfContentByte canvas = stamper.getOverContent(3); // 在第3页写入文本 BaseFont bf = BaseFont.createFont(); canvas.beginText(); canvas.setFontAndSize(bf, 12); canvas.showTextAligned(PdfContentByte.ALIGN_LEFT, "New text", 100, 700, 0); canvas.endText(); // 添加页面 PdfImportedPage page3 = stamper.getImportedPage(reader, 3); stamper.addPage(page3); // 保存修改 stamper.close(); reader.close();
这会读取test.pdf,在第3页添加新文本,并复制第3页新增一个页面,生成test_new.pdf。
iText是一个功能强大的PDF处理库,上面只是简单介绍了一些基本功能,还可以实现PDF的加密、优化、分割和合并等高级操作。
所以,掌握iText可以让我们在Java程序中很好地实现对PDF文件的创建、解析和操作,这在很多业务场景下会派上用场。BeanUtils用法详解,附源码分析
BeanUtils是Apache Commons BeanUtils库提供的一个工具类,用于在Java Bean之间拷贝属性。
它可以拷贝同名的属性,不同名但具有相同getter/setter方法的属性,以及可以通过map映射关系来拷贝的属性。基本用法
public class Source { private String name = "John"; private int age = 30; } public class Target { private String name; private int age; } Source source = new Source(); Target target = new Target(); BeanUtils.copyProperties(source, target); System.out.println(target.getName()); // John System.out.println(target.getAge()); // 30
这里name和age属性通过同名拷贝成功。
拷贝不同名属性
public class Source { private String firstName = "John"; } public class Target { private String name; } BeanUtils.copyProperties(source, target, new String[]{"firstName", "name"}); System.out.println(target.getName()); // John
通过映射数组{"firstName", "name"}指定firstName对应name,实现拷贝。
拷贝Map中的值
Map<String, String> map = new HashMap<>(); map.put("firstName", "John"); map.put("lastName", "Smith"); BeanUtils.populate(target, map);
populate()可以直接从Map中拷贝值到Bean中。
源码分析
BeanUtils的copyProperties()方法主要做了以下工作:
- 获得source和target的所有属性描述符,包括getter/setter方法、属性类型等
- 遍历source的所有属性
- 根据属性名、映射数组或者getter/setter方法,在target中找到对应属性
- 如果类型匹配,则调用target属性的setter方法设置值
- otherwise,尝试类型转换,如String->int等,然后再设置值
其关键源码如下:PropertyDescriptor sourcePd = getPropertyDescriptor(source, propName); PropertyDescriptor targetPd = getPropertyDescriptor(target, propName); if (sourcePd != null && targetPd != null && sourcePd.getPropertyType() == targetPd.getPropertyType()) { // Types match - copy directly copySimpleProperty(source, sourcePd, target, targetPd); } else { // Type conversion needed value = convertType(propName, value, sourcePd, targetPd); copySimpleProperty(value, null, target, targetPd); }
可以看到,它正是通过PropertyDescriptor获取属性元信息,然后根据类型是否匹配来决定是否进行类型转换,这是BeanUtils实现属性拷贝的关键。
所以总体来说,BeanUtils是一个简单实用的工具类,通过反射实现JavaBean之间的属性拷贝,同时支持类型转换,是我们开发中常用的一个类。理解其工作原理,也有助于我们实现类似的功能。