外贸网站模板制作,世界各大网站搜索引擎提交入口,个人网址怎么填,做网站网页需要什么软件六#xff1a;File类与IO流#xff1a;
这里给出三种常见的初始化方法#xff1a; 通过文件路径初始化:
File file new File(C:/example/test.txt);这种方法用于创建一个文件对象#xff0c;该文件对象表示指定路径的文件或目录。例如#xff1a;File fil…六File类与IO流
这里给出三种常见的初始化方法 通过文件路径初始化:
File file new File(C:/example/test.txt);这种方法用于创建一个文件对象该文件对象表示指定路径的文件或目录。例如File file new File(C:/example/test.txt); 表示创建一个指向 “C:/example/test.txt” 的文件对象。
2. 通过文件路径和文件名初始化:
File file new File(C:/example, test.txt);这种方法用于创建一个文件对象该文件对象表示指定目录中的指定文件。例如File file new File(C:/example, test.txt); 表示创建一个指向 “C:/example/test.txt” 的文件对象。 这里有两个参数第一个一定是文件目录可以是String类型也可以是文件类型的参数第二个可以是文件也可以是文件目录。
3. 通过URI初始化:
URI uri new URI(file:///C:/example/test.txt);
File file new File(uri);这种方法用于创建一个文件对象该文件对象表示指定URI的文件或目录。例如URI uri new URI(file:///C:/example/test.txt); File file new File(uri); 表示创建一个指向 “C:/example/test.txt” 的文件对象。
这些方法可用于创建Java文件类的实例并指定文件的路径、名称或URI。 要得到Java文件的相对路径和绝对路径可以使用以下方法 相对路径相对路径是相对于当前工作目录的路径。可以使用以下代码来获取相对路径
String relativePath new File(yourFile.java).getPath();2. 绝对路径绝对路径是文件在文件系统中的完整路径。可以使用以下代码来获取绝对路径
String absolutePath new File(yourFile.java).getAbsolutePath();以上代码中的yourFile.java是你要获取路径的Java文件名。通过这两种方法你可以得到Java文件的相对路径和绝对路径。
File类的方法
1基本方法 举例 使用list()方法列出目录下一级的文件和子目录名称如下所示
import java.io.File;public class ListFilesExample {public static void main(String[] args) {File directory new File(path/to/directory);String[] files directory.list();for (String file : files) {System.out.println(file);}}
}使用listFiles()方法列出目录下一级的文件和子目录的File对象如下所示
import java.io.File;public class ListFilesExample {public static void main(String[] args) {File directory new File(path/to/directory);File[] files directory.listFiles();for (File file : files) {System.out.println(file.getName());}}
}当然以下是完整的输出示例
假设目录下有文件 “file1.txt” 和子目录 “subdir”使用上述两种方法的输出将如下所示
file1.txt
subdir2命名
Java中的renameTo方法用于重命名文件或移动文件到另一个目录。它是File类的一个方法接受一个File对象作为参数表示文件的新路径或新名称。如果重命名或移动成功renameTo方法将返回true否则返回false。 如果你只想改变文件的名称而不改变其路径你可以使用 renameTo() 方法。以下是一个示例展示如何仅仅重命名文件而不移动它
import java.io.File;public class FileRenameExample {public static void main(String[] args) {File originalFile new File(F:\\oldfile.txt); // 原始文件的路径File newFile new File(F:\\newfile.txt); // 新文件的路径if (originalFile.renameTo(newFile)) {System.out.println(文件已重命名);} else {System.out.println(文件无法重命名);}}
}3判断 4创建、删除功能
• public boolean createNewFile() 创建文件。若文件存在则不创建返回false。
• public boolean mkdir() 创建文件目录。如果此文件目录存在就不创建了。如果此文件目录的上层目录不存在也不创建。
• public boolean mkdirs() 创建文件目录。如果上层文件目录不存在一并创建。
• public boolean delete() 删除文件或者文件夹 删除注意事项① Java 中的删除不走回收站。② 要删除一个文件目录请注意该文件目录内不能包含文件或者文件目录。练习 判断指定目录下是否有后缀名为.jpg 的文件。如果有就输出该文件名称
//方法 1
Test
public void test1(){
File srcFile new File(d:\\code);
String[] fileNames srcFile.list();
for(String fileName : fileNames){
if(fileName.endsWith(.jpg)){System.out.println(fileName);}}
}//方法 2
Test
public void test2(){
File srcFile new File(d:\\code);
File[] listFiles srcFile.listFiles();
for(File file : listFiles){
if(file.getName().endsWith(.jpg)){System.out.println(file.getAbsolutePath());}}
}//方法 3
/*
* File 类提供了两个文件过滤器方法
* public String[] list(FilenameFilter filter)
* public File[] listFiles(FileFilter filter)
*/
Test
public void test3(){
File srcFile new File(d:\\code);
File[] subFiles srcFile.listFiles(new FilenameFilter() {
Overridepublic boolean accept(File dir, String name) {return name.endsWith(.jpg);}});for(File file : subFiles){System.out.println(file.getAbsolutePath());}}拓展计算大小/删除所有/遍历目录下面所有文件
以遍历为例子
import java.io.File;public class FileTraversal {public static void main(String[] args) {File directory new File(path_to_directory);traverseDirectory(directory);}public static void traverseDirectory(File directory) {if (directory.isDirectory()) {File[] files directory.listFiles();if (files ! null) {for (File file : files) {if (file.isDirectory()) {traverseDirectory(file);} else {System.out.println(file.getName());}}}}}
}流 FileReader与FileWriter
注意FileReader是输入流而FileWriter是输出流输入流是读取数据的不要搞反了。输入流是从数据源如文件、网络连接等读取数据的流而输出流是向数据目标如文件、网络连接等写入数据的流。
当我们使用FileReader来读取文件时我们创建一个FileReader对象并使用它来读取文件中的数据。这意味着数据从文件流向我们的程序因此FileReader是输入流。
另一方面如果我们想要向文件中写入数据我们会使用FileWriter它是输出流。通过FileWriter我们可以将数据从程序流向文件因此FileWriter是输出流。
FileReader
下面是一个简单的示例演示如何使用FileReader来读取文件中的数据
import java.io.FileReader;
import java.io.IOException;public class FileReaderExample {public static void main(String[] args) {try {// 创建一个FileReader对象指定要读取的文件路径FileReader fileReader new FileReader(example.txt);// 读取文件中的字符数据int character;while ((character fileReader.read()) ! -1) {System.out.print((char) character);}// 关闭文件读取流fileReader.close();} catch (IOException e) {e.printStackTrace();}}
}在上面的示例中我们首先创建了一个FileReader对象指定要读取的文件路径“example.txt”。然后我们使用while循环和read()方法来逐个读取文件中的字符数据并将其打印到控制台上。最后我们关闭了文件读取流。
需要注意的是在使用FileReader时需要处理可能抛出的IOException异常。在示例中我们使用了try-catch块来捕获并处理异常。最好用finally确保一定会关闭流。可以用ctrlAItT快捷操作。
比较规范的写法
Test
public void test2() {
FileReader fr null;
try {
//1. 创建 File 类的对象对应着物理磁盘上的某个文件
File file new File(hello.txt);
//2. 创建 FileReader 流对象将 File 类的对象作为参数传递到FileReader 的构造器中
fr new FileReader(file);
//3. 通过相关流的方法读取文件中的数据
/*
* read():每次从对接的文件中读取一个字符。并将此字符返回。
* 如果返回值为-1,则表示文件到了末尾可以不再读取。
* */
int data;
while ((data fr.read()) ! -1) {
System.out.println((char) data);}
} catch (IOException e) {e.printStackTrace();
} finally {
//4. 关闭相关的流资源避免出现内存泄漏try {if (fr ! null)fr.close();} catch (IOException e) {e.printStackTrace();}}
}if (fr ! null)可以防止流没有创建成功的问题。
当然read()一个一个读太慢了可以用其他的方法读取数据
read(char[] cbuf)
是Java中的一个方法用于从输入流中读取数据到字符数组cbuf中。该方法会尝试从输入流中读取数据并将读取的数据存储到cbuf数组中返回实际读取的字符数。如果输入流中没有数据可供读取则返回-1。
使用该方法时需要先创建一个字符数组cbuf并指定要读取的字符数目。然后调用read(cbuf)方法将数据读取到数组中。通常在循环中使用该方法直到返回-1表示数据读取完毕。
例如
Test
public void test3() {
FileReader fr null;
try {
//1. 创建 File 类的对象对应着物理磁盘上的某个文件
File file new File(hello.txt);
//2. 创建 FileReader 流对象将 File 类的对象作为参数传递到FileReader 的构造器中
fr new FileReader(file);
//3. 通过相关流的方法读取文件中的数据
char[] cbuf new char[5];
/*
* read(char[] cbuf) : 每次将文件中的数据读入到 cbuf 数组中并返回读入到数组中的字符的数。
* */
int len; //记录每次读入的字符的个数
while ((len fr.read(cbuf)) ! -1) {
//处理 char[]数组即可
//错误
// for(int i 0;i cbuf.length;i){
// System.out.print(cbuf[i]);
// }
//错误
// String str new String(cbuf);
// System.out.print(str);
//正确
// for(int i 0;i len;i){
// System.out.print(cbuf[i]);
// }
//正确
String str new String(cbuf, 0, len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭相关的流资源避免出现内存泄漏
try {
if (fr ! null)
fr.close();
} catch (IOException e) {e.printStackTrace();}}}
}FileWriter
文件不在会自动创建。文件如果存在会根据构造器的声明判断比如fwnew FileWriter(file),fwnew FileWriter(file,false),都是覆盖fwnew FileWriter(file,true)是追加等。
下面是 FileWriter 的一些常用方法 public void write(int c) throws IOException将单个字符 c 写入文件。 public void write(char[] c, int offset, int len)将字符数组中从索引 offset 开始、长度为 len 的一部分写入文件。 public void write(String s, int offset, int len)将字符串中从索引 offset 开始、长度为 len 的一部分写入文件。
当然还有一些其他的方法比如直接写入String或者char []。 FileInputStream与FileOutStream
以上均为字节流
字节流是以字节为单位进行读写操作的流它主要用于处理二进制数据如图像、音频、视频等。在Java中字节流的基本类是InputStream和OutputStream它们提供了读取和写入字节的方法。
字符流是以字符为单位进行读写操作的流它主要用于处理文本数据。在Java中字符流的基本类是Reader和Writer它们提供了读取和写入字符的方法。
另外字符流通常会使用指定的字符编码来处理文本数据而字节流则不会考虑字符编码的问题。因此在处理文本数据时通常会使用字符流来避免出现乱码等问题。
也就是说字符流无法实现图片的拷贝。
举例
import java.io.*;public class ImageCopy {public static void main(String[] args) {FileInputStream inputStream null;FileOutputStream outputStream null;try {File inputFile new File(input.jpg);File outputFile new File(output.jpg);inputStream new FileInputStream(inputFile);outputStream new FileOutputStream(outputFile);byte[] buffer new byte[1024];int length;while ((length inputStream.read(buffer)) 0) {outputStream.write(buffer, 0, length);}System.out.println(图片复制成功);} catch (IOException e) {System.out.println(发生IO异常 e.getMessage());} finally {try {if (inputStream ! null) {inputStream.close();}if (outputStream ! null) {outputStream.close();}} catch (IOException e) {System.out.println(关闭流时发生异常 e.getMessage());}}}
}以上都是节点流下面来看看几种处理流
处理流
1缓冲流
缓冲流的作用是在数据传输过程中提供一个缓冲区可以暂时存储数据并进行批量处理从而提高数据传输的效率和性能。缓冲流可以减少对底层资源的频繁访问减少I/O操作的次数减少数据传输的延迟提高数据传输的速度。
另外缓冲流还可以提供一种更方便的方式来处理数据比如可以使用缓冲流的缓冲区来进行数据的预读取和预写入从而提高数据的读写效率。同时缓冲流还可以提供一种更灵活的方式来控制数据的传输比如可以设置缓冲区的大小调整数据传输的速度实现数据的流控制等功能。 注意同一基类具有对应关系。
举例
import java.io.*;public class ImageCopyExample {public static void main(String[] args) throws IOException {FileInputStream in null;FileOutputStream out null;try {File inputFile new File(input.jpg);File outputFile new File(output.jpg);in new FileInputStream(inputFile);out new FileOutputStream(outputFile);BufferedInputStream bufferedInput new BufferedInputStream(in);BufferedOutputStream bufferedOutput new BufferedOutputStream(out);int byteRead;while ((byteRead bufferedInput.read()) ! -1) {bufferedOutput.write(byteRead);}System.out.println(Image copied successfully.);} finally {if ( bufferedInput! null) {bufferedInput.close();}if (bufferedOutput ! null) {bufferedOutput.close();}}}
}注意事实上就是在后面把字节流的名字改为缓冲流的名字注意在进行流的关闭时要先关闭外层流再关闭内层流即先缓冲后字节注意外层流关闭会自动关闭内层所以也可以忽略内层流。
补充readLine():
readLine() 是 Java 中用于读取文本行的方法。它通常与 BufferedReader 类一起使用。以下是使用 readLine() 方法来读取文件内容的示例代码
import java.io.*;public class Main {public static void main(String[] args) {try {BufferedReader in new BufferedReader(new FileReader(test.log));String str;while ((str in.readLine()) ! null) {System.out.println(str);}// Note: The last value of str will be null.} catch (IOException e) {// Handle any exceptions here.}}
}注意该方法会忽略掉换行所以如果需要输出显示器或者写入其他文件要加上换行符或者调用BufferedWriter对象的newLine()方法。
补充flush():
因为内置缓冲区的原因如果 FileWriter 不关闭输出流无法写出字符到文件中。但是关闭的流对象是无法继续写出数据的。如果我们既想写出数据又想继续使用流就需要 flush() 方法了。
• flush() 刷新缓冲区流对象可以继续使用。
• close()先刷新缓冲区然后通知系统释放资源。流对象不可以再被使用了。
注意即便是 flush()方法写出了数据操作的最后还是要调用 close 方法释放系统资源。flush相当于把缓冲区已经有的内容全部写入文件相当于保存了。注意只用reader类才有该方法。
2转换流
转换流是指将一个流转换成另一个流的过程。在Java中可以通过InputStreamReader和OutputStreamWriter来进行流的转换。
InputStreamReader可以将字节流转换成字符流它接受一个字节流作为输入并根据指定的字符编码将其转换成字符流。这样就可以方便地进行字符的读取和处理。
OutputStreamWriter则是将字符流转换成字节流它接受一个字符流作为输入并根据指定的字符编码将其转换成字节流。这样就可以方便地将字符输出到文件或网络中。
通过使用转换流可以在不改变原有流的情况下对流进行字符编码的转换从而方便地进行字符的读写操作。这在处理文件和网络数据时非常有用。 要先解码再进行编码。 举例 同理只用关闭外层即可。
注意InPutStreamReaderOutPutStreamReader还可以接受一个字符集参数在本例中输出正常因为IDEA默认就是UTF-8不需要显式的写出来。 举例GBK-UTF-8编码的转换
/**
\* author
\* create 9:06
*/
public class InputStreamReaderDemo {
Test
public void test() {
InputStreamReader isr null;
OutputStreamWriter osw null;try {isr new InputStreamReader(new FileInputStream(康师傅的话.txt),gbk);osw new OutputStreamWriter(newFileOutputStream(C:\\Users\\shkstart\\Desktop\\寄语.txt),utf-8);
char[] cbuf new char[1024];
int len;
while ((len isr.read(cbuf)) ! -1) {osw.write(cbuf, 0, len);osw.flush();
}
System.out.println(文件复制完成);
} catch (IOException e) {e.printStackTrace();
} finally {
try {if (isr ! null)
isr.close();
} catch (IOException e) {
e.printStackTrace();}
try {
if (osw ! null)osw.close();
} catch (IOException e) {e.printStackTrace();}}}
}注意最初的文件编码方式为GBK所以只能使用GBK解码。
字符总结 3数据流与对象流
数据流
数据流是Java中用于读写基本数据类型和字符串的一种流。它提供了一种方便的方式来读写Java的基本数据类型如int、double、boolean等以及字符串。
数据流包括DataInputStream和DataOutputStream两个类。DataOutputStream可以将基本数据类型和字符串写入到输出流中而DataInputStream可以从输入流中读取基本数据类型和字符串。
使用数据流进行数据的读写时可以方便地将各种基本数据类型和字符串写入到输出流中并且可以从输入流中读取这些数据。这样可以方便地在不同的系统之间传输数据而不用担心数据类型的兼容性问题。 但在实际中我们更喜欢用对象流因为它还可以读取引用类型变量。
对象流
对象流是Java中用于读写对象的一种流。它可以将对象以二进制的形式进行读写可以用于在网络上传输对象或者将对象保存到文件中。
对象流包括ObjectInputStream和ObjectOutputStream两个类。ObjectOutputStream可以将对象写入到输出流中而ObjectInputStream可以从输入流中读取对象。
使用对象流进行对象的读写时需要注意对象的序列化和反序列化。对象需要实现Serializable接口以便可以被序列化成二进制形式并且可以被反序列化还原成对象。
对象流的使用非常灵活可以用于传输各种类型的对象包括自定义的对象。它为Java中对象的读写提供了方便的方式同时也可以用于实现对象的持久化和网络传输。 以下是一个简单的Java序列化和反序列化的示例。这个例子中我们有一个名为Person的类它实现了Serializable接口。然后我们创建了一个Person对象将其序列化到一个文件中然后再从该文件中反序列化该对象。
import java.io.*;class Person implements Serializable {public String name;public int age;public Person(String name, int age) {this.name name;this.age age;}
}public class Main {public static void main(String[] args) {Person person new Person(John Doe, 30);// 序列化try {FileOutputStream fileOut new FileOutputStream(./person.ser);ObjectOutputStream out new ObjectOutputStream(fileOut);out.writeObject(person);out.close();fileOut.close();System.out.printf(Serialized data is saved in ./person.ser);} catch (IOException i) {i.printStackTrace();}// 反序列化Person deserializedPerson null;try {FileInputStream fileIn new FileInputStream(./person.ser);ObjectInputStream in new ObjectInputStream(fileIn);deserializedPerson (Person) in.readObject();in.close();fileIn.close();} catch (IOException i) {i.printStackTrace();return;} catch (ClassNotFoundException c) {System.out.println(Person class not found);c.printStackTrace();return;}System.out.println(Deserialized Person...);System.out.println(Name: deserializedPerson.name);System.out.println(Age: deserializedPerson.age);}
}ObjectOutputStream out new ObjectOutputStream(fileOut); 这行代码只是创建了一个 ObjectOutputStream 对象这个对象可以用来将Java对象序列化并写入到一个输出流中。在这个例子中输出流是一个文件输出流它指向 ./person.ser 文件。
真正的序列化过程发生在 out.writeObject(person); 这行代码执行时。这行代码将 person 对象序列化并将序列化后的数据写入到 ObjectOutputStream 关联的输出流中也就是 ./person.ser 文件。
所以当 out.writeObject(person); 这行代码执行完成后person 对象才被序列化并写入到 ./person.ser 文件中。在这之前./person.ser 文件要么不存在要么是空的或者包含了之前写入的数据。
注意ObjectOutputStream也是输出流所以也有flush方法道理同上。 注意类中静态的属性或者是声明为transient的属性不会序列化。
4其他流
4.1.标准输入输出流 如图 即不用Scanner方法也实现了读入。
4.2.打印流
如图 即向io.txt文件中写入注意调用了setOut方法让输出不在控制台上面显示而是直接写进文件中。
apache-common包
Apache Commons是一个可重复使用的Java组件集合为Java应用程序提供常用功能。Apache Commons项目是Apache软件基金会的一部分旨在提供高质量、可重复使用的Java组件这些组件是免费提供的。
Apache Commons包括各种组件如集合、配置、IO、语言、数学等。这些组件提供了常见编程问题的解决方案并帮助开发人员编写更干净、更高效的代码。
Apache Commons中的组件通常被打包成JARJava Archive文件以便于在Java应用程序中使用。JAR文件是一种用于打包Java类、资源和元数据的标准文件格式。当开发人员需要使用Apache Commons中的组件时他们通常会将相应的JAR文件添加到他们的项目中并在代码中引用这些组件。
因此Apache Commons包实际上是一组JAR文件的集合每个JAR文件包含一个或多个相关的组件。开发人员可以通过将这些JAR文件添加到他们的Java项目中来使用Apache Commons中的组件从而为他们的应用程序提供各种常用功能和解决方案。
七网络编程
7.1.软件架构
客户端-服务器CS架构是一种传统的架构模式其中客户端应用程序通过网络连接到服务器客户端负责处理用户界面和用户输入而服务器负责处理业务逻辑和数据存储。这种架构适用于需要复杂业务逻辑和数据处理的应用如企业级应用、数据库系统等。
浏览器-服务器BS架构是一种Web应用程序的架构模式其中浏览器作为客户端通过HTTP协议向服务器请求页面和资源服务器端负责处理业务逻辑和数据存储并将结果以HTML等格式返回给客户端。这种架构适用于互联网应用、电子商务网站等Web应用。
两种架构的主要区别在于CS架构更加灵活和复杂适用于需要处理复杂业务逻辑和数据的应用而BS架构更加简单和易于部署适用于互联网应用和Web网站。同时CS架构需要安装客户端应用程序而BS架构只需要浏览器即可访问应用。
7.2.计算机网络
计算机网络是指将多台计算机通过通信设备连接起来使它们之间可以相互传输数据和共享资源的系统。计算机网络可以是局域网LAN、城域网MAN、广域网WAN或者因特网Internet。它们通过各种通信协议和技术来实现数据的传输和通信。计算机网络的应用非常广泛包括文件共享、打印共享、电子邮件、网上购物、在线游戏等。 三大要素
7.2.1.IP地址
IP是Internet Protocol的缩写是互联网协议的一种。它是一种用于在网络上进行数据传输的协议通过它数据包可以在网络上进行传输。IP地址是用来标识网络上的设备的唯一地址类似于人类的家庭地址。
IP地址根据不同的特征可以分为IPv4和IPv6两种类型。IPv4地址是32位的地址通常用四个十进制数表示每个数的取值范围是0-255例如192.168.1.1。而IPv6地址是128位的地址通常用八组十六进制数表示每组数之间用冒号分隔例如2001:0db8:85a3:0000:0000:8a2e:0370:7334。IPv6地址是128位的128位等于16字节因为1字节等于8位所以128位就是16字节。
根据IP地址的分配方式IP地址又可以分为公网IP(万维网)和私网IP(局域网)。公网IP是可以直接被互联网访问的IP地址而私网IP是在局域网内使用的IP地址不能直接被互联网访问。
补充本地回路地址127.0.0.1。
补充域名 7.2.2.端口号
端口号是用于标识不同网络应用程序的数字标识符。在计算机网络中端口号被用于区分不同的网络应用程序或服务以便数据包可以被正确地路由到目标应用程序。
端口号的范围是从0到65535其中0到1023是被称为“系统端口”或“众所周知的端口”通常用于一些常见的网络服务比如HTTP端口号80、FTP端口号21、SSH端口号22等。而1024到49151之间的端口号被称为“注册端口”用于一些常见的应用程序。而49152到65535之间的端口号则被称为“动态或私有端口”通常用于客户端应用程序或临时服务。
端口号的作用是确保数据包能够被正确地传送到目标应用程序从而实现网络通信和数据交换。在网络通信中发送端和接收端的应用程序需要使用相同的端口号才能进行通信。因此端口号在网络通信中起着非常重要的作用。 7.2.3.网络通信协议
网络通信协议是计算机网络中用于在不同设备之间传输数据的规则和约定。它定义了数据的格式、传输方式、错误检测和纠正等方面的规范以确保不同设备之间能够相互通信。
常见的网络通信协议包括TCP/IP协议、HTTP协议、FTP协议、SMTP协议、POP3协议等。其中TCP/IP协议是最为重要和基础的网络通信协议它是互联网的基础协议用于在不同设备之间进行数据传输和通信。 7.3.InetAddress getByName()和getLocalHost()是Java中两个不同的方法它们在不同的类和场景中使用。 getByName()方法 这是java.net.InetAddress类中的一个方法用于获取与指定主机名或IP地址对应的InetAddress对象。该方法可以接受一个主机名或IP地址字符串作为参数并返回一个表示该地址的InetAddress对象。如果传入的参数为null则返回表示本地主机地址的InetAddress对象。
try {InetAddress ia InetAddress.getByName(46.21.29.40);System.out.println(ia.getHostName());
} catch (Exception ex) {System.err.println(ex);
}注意也可以传入域名。
2.getLocalHost()方法这个方法用于获取表示本地主机的InetAddress对象。例如
InetAddress localHost InetAddress.getLocalHost();
System.out.println(本机的IP地址 localHost.getHostAddress());再来看看两个常用方法
getHostName() 和 getHostAddress() 是 Java 中 InetAddress 类的两个方法。
getHostName() 方法用于获取主机名例如如果我们有一个 InetAddress 对象 ip我们可以通过调用 ip.getHostName() 来获取该主机的主机名。getHostAddress() 方法用于获取 IP 地址同样如果我们有一个 InetAddress 对象 ip我们可以通过调用 ip.getHostAddress() 来获取该主机的 IP 地址。
import java.net.InetAddress;
import java.net.UnknownHostException;public class Address {public static void main(String[] args) {InetAddress ip;try {ip InetAddress.getLocalHost();String localname ip.getHostName(); // 获取本机名String localip ip.getHostAddress(); // 获取本机地址System.out.println(本机名 localname);System.out.println(本机地址 localip);} catch (UnknownHostException e) {System.out.println(主机不存在或网络连接错误);e.printStackTrace();}}
}7.4.传输层协议TCP与UDP协议
TCP传输控制协议是一种面向连接的、可靠的、基于字节流的传输层协议。它提供了数据传输的可靠性和顺序性通过三次握手建立连接和四次挥手断开连接来确保数据的可靠传输。TCP还具有拥塞控制和流量控制的功能可以根据网络状况调整数据传输的速率以保证网络的稳定性和效率。TCP适用于对数据传输可靠性要求较高的应用场景如网页浏览、文件传输等。
UDP用户数据报协议是一种面向无连接的、不可靠的传输层协议。它通过数据包的形式进行数据传输不保证数据的可靠性和顺序性。UDP的优点是传输速度快适用于对数据传输实时性要求较高的应用场景如音视频传输、实时游戏等。但由于UDP不提供可靠性保证因此在数据传输过程中可能会丢包或出现乱序需要应用层自行处理。UDP适用于对数据传输实时性要求较高、对数据丢失和乱序容忍度较高的应用场合。 7.5.Socket类 7.5.1.Socket 相关类 API
7.5.1.1.ServerSocket 类
//ServerSocket 类的构造方法
• ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
//ServerSocket 类的常用方法
• Socket accept()侦听并接受到此套接字的连接。7.5.1.2.Socket 类
//Socket 类的常用构造方法
• public Socket(InetAddress address,int port)创建一个流套接字并将其连接到指定IP地址的指定端口号。
• public Socket(String host,int port)创建一个流套接字并将其连接到指定主机上的指定端口号。
//Socket 类的常用方法
• public InputStream getInputStream()返回此套接字的输入流可以用于接收消息
• public OutputStream getOutputStream()返回此套接字的输出流可以用于发送消息
• public InetAddress getInetAddress()此套接字连接到的远程 IP 地址如果套接字是未连接的则返回 null。
• public InetAddress getLocalAddress()获取套接字绑定的本地地址。
• public int getPort()此套接字连接到的远程端口号如果尚未连接套接字则返回0。
• public int getLocalPort()返回此套接字绑定到的本地端口。如果尚未绑定套接字则返回 -1。
• public void close()关闭此套接字。套接字被关闭后便不可在以后的网络连接中使用即无法重新连接或重新绑定。需要创建新的套接字对象。 关闭此套接字也将会关闭该套接字的 InputStream 和 OutputStream。
• public void shutdownInput()如果在套接字上调用 shutdownInput() 后从套接字输入流读取内容则流将返回 EOF文件结束符。 即不能在从此套接字的输入流中接收任何数据。
• public void shutdownOutput()禁用此套接字的输出流。对于 TCP 套接字任何以前写入的数据都将被发送并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流则该流将抛出 IOException。 即不能通过此套接字的输出流发送任何数据。注意先后调用 Socket 的 shutdownInput()和 shutdownOutput()方法仅仅关闭了输入流和输出流并不等于调用 Socket 的 close()方法。在通信结束后仍然要调用 Scoket 的 close()方法因为只有该方法才会释放 Socket 占用的资源比如占用的本地端口号等。
7.6.TCP网络编程 客户端程序包含以下四个基本的步骤
• 创建 Socket 根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端响应则建立客户端到服务器的通信线路。若连接失败会出现异常。
• 打开连接到 Socket 的输入/ 出流 使用 getInputStream()方法获得输入流使用getOutputStream()方法获得输出流进行数据传输
• 按照一定的协议对 Socket 进行读/ 写操作通过输入流读取服务器放入线路的信息但不能读取自己放入线路的信息通过输出流将信息写入线路。
• 关闭 Socket 断开客户端到服务器的连接释放线路
服务器端程序包含以下四个基本的 步骤
• 调用 ServerSocket(int port) 创建一个服务器端套接字并绑定到指定端口上。用于监听客户端的请求。
• 调用 accept() 监听连接请求如果客户端请求连接则接受连接返回通信套接字对象。
• 调用 该 Socket 类对象的 getOutputStream() 和 getInputStream () 获取输出流和输入流开始网络数据的发送和接收。
• 关闭 Socket 对象客户端访问结束关闭通信套接字。
举例客户端发送内容给服务端服务端将内容打印到控制台上。
客户端代码
public class Client {
public static void main(String[] args) throws Exception {
// 1、准备 Socket连接服务器需要指定服务器的 IP 地址和端口号
Socket socket new Socket(127.0.0.1, 8888);
// 2、获取输出流用来发送数据给服务器
OutputStream out socket.getOutputStream();
// 发送数据
out.write(lalala.getBytes());
//会在流末尾写入一个“流的末尾”标记对方才能读到-1否则对方的读取方法会一致阻塞
socket.shutdownOutput();
//3、获取输入流用来接收服务器发送给该客户端的数据
InputStream input socket.getInputStream();
// 接收数据
byte[] data new byte[1024];
StringBuilder s new StringBuilder();
int len;
while ((len input.read(data)) ! -1) {
s.append(new String(data, 0, len));
}
System.out.println(服务器返回的消息是 s);
//4、关闭 socket不再与服务器通信即断开与服务器的连接
//socket 关闭意味着 InputStream 和 OutputStream 也关闭了
socket.close();
}
}注意socket.shutdownOutput();必须要有这一步防止客户端仍在继续尝试发送数据。
服务端代码
public class Server {
public static void main(String[] args)throws Exception {
//1、准备一个 ServerSocket 对象并绑定 8888 端口
ServerSocket server new ServerSocket(8888);
System.out.println(等待连接....);
//2、在 8888 端口监听客户端的连接该方法是个阻塞的方法如果没有客户端连接将一直等待
Socket socket server.accept();
InetAddress inetAddress socket.getInetAddress();
System.out.println(inetAddress.getHostAddress() 客户端连接成功);
//3、获取输入流用来接收该客户端发送给服务器的数据
InputStream input socket.getInputStream();
//接收数据
byte[] data new byte[1024];
StringBuilder s new StringBuilder();
int len;
while ((len input.read(data)) ! -1) {
s.append(new String(data, 0, len));
}
System.out.println(inetAddress.getHostAddress() 客户端发送的消息是 s);
//4、获取输出流用来发送数据给该客户端
OutputStream out socket.getOutputStream();
//发送数据
out.write(欢迎登录.getBytes());
out.flush();
//5、关闭 socket不再与该客户端通信
//socket 关闭意味着 InputStream 和 OutputStream 也关闭了
socket.close();
//6、如果不再接收任何客户端通信可以关闭 ServerSocket
server.close();}
}注意可以通过socket.getInetAddress().getHostAddress()获取到是谁在进行连接。
注意有些边读边输出的方法可能会出现乱码比如字节流存入数组对原来的文本有截断则会出现乱码可以通过ByteArrayOutputStream解决代码如下
byte[] data new byte[1024];
int len;
ByteArrayOutputStream baos new ByteArrayOutputStream();
while ((len input.read(data)) ! -1) {baos.write(data,0,len);
}
System.out.println(inetAddress.getHostAddress() 客户端发送的消息是 baos.toString());相当于把每部分写入baos中最后一起输出。
7.7.UDP网络编程
UDP(User Datagram Protocol用户数据报协议)是一个无连接的传输层协议、提供面向事务的简单不可靠的信息传送服务类似于短信。
发送端程序包含以下四个基本的步骤
• 创建 DatagramSocket 默认使用系统随机分配端口号。
• 创建 DatagramPacket将要发送的数据用字节数组表示并指定要发送的数据长度接收方的IP 地址和端口号。
• 调用 该 DatagramSocket 类对象的 send 方法 发送数据报 DatagramPacket 对象。
• 关闭 DatagramSocket 对象发送端程序结束关闭通信套接字。接收端程序包含以下四个基本的步骤
• 创建 DatagramSocket 指定监听的端口号。
• 创建 DatagramPacket指定接收数据用的字节数组起到临时数据缓冲区的效果并指定最大可以接收的数据长度。
• 调用 该 DatagramSocket 类对象的 receive 方法 接收数据报 DatagramPacket 对象。
• 关闭 DatagramSocket 接收端程序结束关闭通信套接字。举例
发送端
DatagramSocket ds null;
try {ds new DatagramSocket();byte[] by hello,atguigu.com.getBytes();DatagramPacket dp new DatagramPacket(by, 0, by.length,InetAddress.getByName(127.0.0.1), 10000);
ds.send(dp);
} catch (Exception e) {e.printStackTrace();
} finally {
if (ds ! null)ds.close();
}接收端
DatagramSocket ds null;
try {
ds new DatagramSocket(10000);
byte[] by new byte[1024*64];
DatagramPacket dp new DatagramPacket(by, by.length);
ds.receive(dp);
String str new String(dp.getData(), 0, dp.getLength());
System.out.println(str -- dp.getAddress());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ds ! null)
ds.close();
}7.8.URL网络编程
URL(Uniform Resource Locator)统一资源定位符它表示 Internet 上某一资源的地址。
• 通过 URL 我们可以访问 Internet 上的各种网络资源比如最常见的 wwwftp 站点。浏览器通过解析给定的 URL 可以在网络上查找相应的文件或其他资源。
• URL 的基本结构由 5 部分组成传输协议://主机名:端口号/文件名#片段名?参数列表
• 例如:http://192.168.1.100:8080/helloworld/index.jsp#a?usernameshkstartpassword123
URL 类常用方法
一个 URL 对象生成后其属性是不能被改变的但可以通过它给定的方法来获取这些属性
• public String getProtocol( ) 获取该 URL 的协议名
• public String getHost( ) 获取该 URL 的主机名
• public String getPort( ) 获取该 URL 的端口号
• public String getPath( ) 获取该 URL 的文件路径
• public String getFile( ) 获取该 URL 的文件名
• public String getQuery( ) 获取该 URL 的查询名八反射机制
Java 程序中所有的对象都有两种类型
编译时类型和运行时类型而很多时候对象的编译时类型和运行时类型不一致。 Object obj new String(“hello”);obj.getClass()。
例如某些变量或形参的声明类型是 Object 类型但是程序却需要调用该对象运行时类型的方法该方法不是 Object 中的方法那么如何解决呢
解决这个问题有两种方案
方案 1在编译和运行时都完全知道类型的具体信息在这种情况下我们可以直接先使用 instanceof 运算符进行判断再利用强制类型转换符将其转换成运行时类型的变量即可。
方案 2编译时根本无法预知该对象和类的真实信息程序只能依靠运行时信息来发现该对象和类的真实信息这就必须使用反射。
反射概述
Reflection反射是被视为动态语言的关键反射机制允许程序在运行期间借助于 Reflection API 取得任何类的内部信息并能直接操作任意对象的内部属性及方法。
反射可以打破封装性。
Java 反射机制提供的功能
• 在运行时判断任意一个对象所属的类
• 在运行时构造任意一个类的对象
• 在运行时判断任意一个类所具有的成员变量和方法
• 在运行时获取泛型信息
• 在运行时调用任意一个对象的成员变量和方法
• 在运行时处理注解
• 生成动态代理
下面都可以获取Class类
(1class外部类成员(成员内部类静态内部类)局部内部类匿名内部 类 2interface接口 3[]数组 4enum枚举 5annotation 注解interface 6primitive type基本数据类型 7void
8.1.获取 Class 类的实例(四种方法)
方式 1要求编译期间已知类型
前提若已知具体的类通过类的 class 属性获取该方法最为安全可靠程序性能最高。
实例
Class clazz String.class;方式 2获取对象的运行时类型
前提已知某个类的实例调用该实例的 getClass()方法获取 Class 对象
实例
Class clazz www.atguigu.com.getClass();方式 3可以获取编译期间未知的类型
前提已知一个类的全类名且该类在类路径下可通过 Class 类的静态方法forName()获取可能抛出 ClassNotFoundException
实例
Class clazz Class.forName(java.lang.String);当使用Class.forName()方法时我们需要传入一个字符串参数该参数是类的全限定名包括包名。该方法会返回一个Class对象该对象代表了对应类的运行时类型信息。
方式 4其他方式(不做要求)
前提可以用系统类加载对象或自定义加载器对象加载指定路径下的类型
实例
ClassLoader cl this.getClass().getClassLoader();
Class clazz4 cl.loadClass(类的全类名);注意两个相同的类的Class对象是相等的因为类只会加载一次。
8.2.Class类的理解 使用了类的加载器加载到内存的方法区。
8.3.类的加载过程与类的加载器 同样的加载器只能加载一次不同的加载器可以加载多次。
查看某个类的类加载器对象
1获取默认的系统类加载器
ClassLoader classloader ClassLoader.getSystemClassLoader();2查看某个类是哪个类加载器加载的
ClassLoader classloader Class.forName(exer2.ClassloaderDemo).getClassLoader();
//如果是根加载器加载的类则会得到 null
ClassLoader classloader1
Class.forName(java.lang.Object).getClassLoader();3获取某个类加载器的父加载器
ClassLoader parentClassloader classloader.getParent();使用 ClassLoader 获取流
关于类加载器的一个主要方法getResourceAsStream(String str):获取类路径下的指定文件的输入流
Test
public void test5() throws IOException {
Properties pros new Properties();
//方式 1此时默认的相对路径是当前的 module
// FileInputStream is new FileInputStream(info.properties);
// FileInputStream is new FileInputStream(src//info1.properties);
//方式 2使用类的加载器
//此时默认的相对路径是当前 module 的 src 目录
InputStream is
ClassLoader.getSystemClassLoader().getResourceAsStream(info1.properties);
pros.load(is);
//获取配置文件中的信息
String name pros.getProperty(name);
String password pros.getProperty(password);
System.out.println(name name , password password);
}8.4.反射的运用
4.1 应用 1创建运行时类的对象
这是反射机制应用最多的地方。创建运行时类的对象有两种方式
方式 1直接调用 Class 对象的 newInstance()方法
要 求 1类必须有一个无参数的构造器。2类的构造器的访问权限需要足够。
方式 2通过获取构造器对象来进行实例化
方式一的步骤
1获取该类型的 Class 对象 2调用 Class 对象的 newInstance()方法创建对象
方式二的步骤
1通过 Class 类的 getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器 2向构造器的形参中传递一个对象数组进去里面包含了构造器中所需的各个参数。 3通过 Constructor 实例化对象。如果构造器的权限修饰符修饰的范围不可见也可以调setAccessible(true)
示例代码
package com.atguigu.reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
public class TestCreateObject {
Test
public void test1() throws Exception{
// AtGuiguClass obj new AtGuiguClass();//编译期间无法创建
Class? clazz Class.forName(com.atguigu.ext.demo.AtGuiguClass);
//clazz 代表 com.atguigu.ext.demo.AtGuiguClass 类型
//clazz.newInstance()创建的就是 AtGuiguClass 的对象
Object obj clazz.newInstance();
System.out.println(obj);
}
Test
public void test2()throws Exception{
Class? clazz Class.forName(com.atguigu.ext.demo.AtGuiguDemo);
//java.lang.InstantiationException:
com.atguigu.ext.demo.AtGuiguDemo
//Caused by: java.lang.NoSuchMethodException:
com.atguigu.ext.demo.AtGuiguDemo.init()//即说明 AtGuiguDemo 没有无参构造就没有无参实例初始化方法init
Object stu clazz.newInstance();
System.out.println(stu);
}
Test
public void test3()throws Exception{
//(1)获取 Class 对象
Class? clazz Class.forName(com.atguigu.ext.demo.AtGuiguDemo);
/*
\* 获取 AtGuiguDemo 类型中的有参构造
\* 如果构造器有多个我们通常是根据形参【类型】列表来获取指定的一个
构造器的
\* 例如public AtGuiguDemo(String title, int num)
*/
//(2)获取构造器对象
Constructor? constructor
clazz.getDeclaredConstructor(String.class,int.class);
//(3)创建实例对象
// T newInstance(Object... initargs) 这个 Object...是在创建对象时给有参构造的实参列表
Object obj constructor.newInstance(尚硅谷,2022);
System.out.println(obj);
}
}4.2. 应用 2获取运行时类的完整结构
可以获取包、修饰符、类型名、父类包括泛型父类、父接口包括泛型父接口、成员属性、构造器、方法、注解类上的、方法上的、属性上的。
//1.实现的全部接口
public Class?[] getInterfaces()
//确定此对象所表示的类或接口实现的接口。
//2.所继承的父类
public Class? Super T getSuperclass()
//返回表示此 Class 所表示的实体类、接口、基本类型的父类的 Class。
//3.全部的构造器
public ConstructorT[] getConstructors()
//返回此 Class 对象所表示的类的所有 public 构造方法。
public ConstructorT[] getDeclaredConstructors()
//返回此 Class 对象表示的类声明的所有构造方法。
//Constructor 类中
//取得修饰符:
public int getModifiers();
//取得方法名称:
public String getName();
//取得参数的类型
public Class?[] getParameterTypes();
//4.全部的方法
public Method[] getDeclaredMethods()
//返回此 Class 对象所表示的类或接口的全部方法
public Method[] getMethods()
//返回此 Class 对象所表示的类或接口的 public 的方法
//Method 类中
public Class? getReturnType()
//取得全部的返回值
public Class?[] getParameterTypes()
//取得全部的参数
public int getModifiers()
//取得修饰符
public Class?[] getExceptionTypes()
//取得异常信息
//5.全部的 Field
public Field[] getFields()
//返回此 Class 对象所表示的类或接口的 public 的 Field。
public Field[] getDeclaredFields()
//返回此 Class 对象所表示的类或接口的全部 Field。
//Field 方法中
public int getModifiers()
//以整数形式返回此 Field 的修饰符
public Class? getType()
//得到 Field 的属性类型
public String getName()
//返回 Field 的名称。
//6. Annotation 相关
get Annotation(ClassT annotationClass)
getDeclaredAnnotations()
//7.泛型相关
//获取父类泛型类型
Type getGenericSuperclass()
//泛型类型ParameterizedType
//获取实际的泛型类型参数数组
getActualTypeArguments()
//8.类所在的包
Package getPackage()4.3. 应用 3调用运行时类的指定结构:
4.3.1 调用指定的属性
在反射机制中可以直接通过 Field 类操作类中的属性通过 Field 类提供的set()和 get()方法就可以完成设置和取得属性内容的操作。
1获取该类型的 Class 对象
Class clazz Class.forName(包.类名);2获取属性对象
Field field clazz.getDeclaredField(属性名);getDeclaredField可以获取所以属性而getField只能获取public属性。
3如果属性的权限修饰符不是 public那么需要设置属性可访问
field.setAccessible(true);4创建实例对象如果操作的是非静态属性需要创建实例对象
Object obj clazz.newInstance(); //有公共的无参构造
Object obj 构造器对象.newInstance(实参...);//通过特定构造器对象创建实例对象4设置指定对象 obj 上此 Field 的属性内容
field.set(obj,属性值);如果操作静态变量那么实例对象可以省略用 null 表示
5取得指定对象 obj 上此 Field 的属性内容
Object value field.get(obj);如果操作静态变量那么实例对象可以省略用 null 表示
4.3.2 调用指定的方法:
1获取该类型的 Class 对象
Class clazz Class.forName(包.类名);2获取方法对象
Method method clazz.getDeclaredMethod(方法名,方法的形参类型列表);形参列表要写成String.class等。
3创建实例对象
Object obj clazz.newInstance();4调用方法
Object result method.invoke(obj, 方法的实参值列表);如果方法的权限修饰符修饰的范围不可见也可以调用setAccessible(true),如果方法是静态方法实例对象也可以省略用 null 代替。
4.3.3.调用指定构造器
在Java的反射机制中可以使用Class类的newInstance()方法来调用指定构造器来创建对象。newInstance()方法会调用类的默认构造器来创建对象如果需要调用指定的构造器可以使用Constructor类的newInstance()方法来实现。
常用的方法包括 getConstructor(Class… parameterTypes)根据参数类型获取指定的构造器。 getConstructors()获取所有公共的构造器。 getDeclaredConstructor(Class… parameterTypes)根据参数类型获取指定的构造器包括私有构造器。 getDeclaredConstructors()获取所有的构造器包括私有构造器。 newInstance(Object… initargs)使用指定的参数调用构造器来创建对象。 setAccessible(boolean flag)设置构造器的可访问性如果构造器是私有的需要设置为true才能调用。
用指定构造器时可以通过以下示例代码来实现
import java.lang.reflect.Constructor;public class ReflectionExample {public static void main(String[] args) {try {// 获取Class对象Class? clazz Class.forName(com.example.MyClass);// 获取指定参数类型的构造器Constructor? constructor clazz.getConstructor(String.class, int.class);// 使用构造器创建对象Object obj constructor.newInstance(example, 123);// 打印对象System.out.println(obj);} catch (Exception e) {e.printStackTrace();}}
}在上面的示例中我们首先获取了MyClass类的Class对象然后通过getConstructor()方法获取了一个带有String和int参数的构造器。接着使用newInstance()方法调用该构造器来创建一个MyClass对象并将其打印出来。
4.3.4.调用注解
一个完整的注解应该包含三个部分 1声明 2使用 3读取。
1声明自定义注解
package com.atguigu.annotation;
import java.lang.annotation.*;
Inherited
Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
public interface Table {
String value();
}让我们来分解这段代码 Inherited: 这个注解表明该注解可以被子类继承。如果一个类使用了带有Inherited注解的注解那么它的子类也会继承这个注解。 Target(ElementType.TYPE): 这个注解指定了注解的作用目标。在这里ElementType.TYPE表示这个注解可以用在类、接口、枚举或注解类型上。 Retention(RetentionPolicy.RUNTIME): 这个注解指定了注解的生命周期。在这里RetentionPolicy.RUNTIME表示这个注解在运行时仍然可用这意味着可以通过反射来获取这个注解的信息。 public interface Table { String value(); }: 这是注解的声明部分。它使用了关键字interface来声明一个注解并定义了一个名为Table的注解。在这个注解中有一个名为value的成员它没有指定默认值因此在使用这个注解时需要为value成员提供值。
总的来说这段代码定义了一个可以用在类上的注解Table它可以被子类继承在运行时可用并且需要提供一个value值。这个注解可以用来为类添加表名的元数据信息例如
Table(user_table)
public class User {// 类的定义
}• 自定义注解可以通过四个元注解Retention,TargetInherited,Documented分别说明它的声明周期使用位置是否被继承是否被生成到 API 文档中。
• Annotation 的成员在 Annotation 定义中以无参数有返回值的抽象方法的形式来声明我们又称为配置参数。返回值类型只能是八种基本数据类型、String 类型、Class 类型、enum 类型、Annotation 类型、以上所有类型的数组。
• 可以使用 default 关键字为抽象方法指定默认返回值。
• 如果定义的注解含有抽象方法那么使用时必须指定返回值除非它有默认值。格式是“方法名 返回值”如果只有一个抽象方法需要赋值且方法名为 value可以省略“value”所以如果注解只有一个抽象方法成员建议使用方法名 value。
2使用注解
Table(t_stu)
public class Student {
Column(columnName sid,columnType int)
private int id;
Column(columnName sname,columnType varchar(20))
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name name;
}
Override
public String toString() {
return Student{ id id , name name \ };}
}这段代码展示了一个使用自定义注解的Java类。让我们逐步解释这段代码的含义
Table(t_stu)
public class Student {在这里Table(“t_stu”)注解被应用在Student类上它为Student类添加了一个名为t_stu的元数据信息可能表示这个类对应数据库中的t_stu表。
Column(columnName sid, columnType int)
private int id;
Column(columnName sname, columnType varchar(20))
private String name;在这里Column注解被应用在类的成员变量上它为id和name成员变量添加了元数据信息包括字段名和字段类型。
3读取和处理自定义注解
自定义注解必须配上注解的信息处理流程才有意义。我们自己定义的注解只能使用反射的代码读取。所以自定义注解的声明周期必须是 RetentionPolicy.RUNTIME。
package com.atguigu.annotation;
import java.lang.reflect.Field;
public class TestAnnotation {
public static void main(String[] args) {
Class studentClass Student.class;
Table tableAnnotation (Table) studentClass.getAnnotation(Table.class);
String tableName ;
if(tableAnnotation ! null){tableName tableAnnotation.value();
}
Field[] declaredFields studentClass.getDeclaredFields();
String[] columns new String[declaredFields.length];
int index 0;
for (Field declaredField : declaredFields) {
Column column declaredField.getAnnotation(Column.class);
if(column! null) {
columns[index] column.columnName();}
}
String sql select ;
for (int i0; iindex; i) {
sql columns[i];
if(iindex-1){sql ,;
}
}
sql from tableName;
System.out.println(sql sql);
}
}这段代码是一个简单的示例演示了如何使用Java的反射机制和注解来生成SQL查询语句。
首先通过反射获取了Student类的Class对象并通过该对象获取了Table注解的值即表名。然后再通过反射获取了Student类的所有字段并遍历每个字段获取其对应的Column注解的值即列名。最后根据获取到的表名和列名拼接成了一个简单的SQL查询语句。
这个示例展示了如何使用注解来标记表名和列名然后通过反射来动态地获取这些注解的值从而生成相应的SQL语句。这种方式可以使代码更加灵活和可维护同时也提高了代码的复用性。