网络编程
1 基本知识
网络编程解决的问题:计算机与计算机之间的通讯问题。
计算机网络:将分布在不同地域的计算机通过外置设备链接起来,计算机之间达到了消息交互、文件传输的功能。
计算机网络通讯的三要素:
1、ip地址、端口号、协议(udp\tcp) |
IP地址:实际上是一个32位的二进制数组成的。 2^8 =255
00000000-00000000-00000000-00000000
IP地址 = 网络号 + 主机号。
IP地址类别:
A类:一个网络号+三个主机号 2^24 政府部门
B类:两个网络号+两个主机号 学校或者事业单位
C类:三个网络号+一个主机
2 InetAddress常用方法
InetAddress 的使用方法: getLocalHost() 返回本机IP地址对象。 getHostAddress() 返回IP地址的字符串表示形式。 getHostName() 获取主机名 getByName() 根据主机名、ip地址生产一个IP地址 |
端口号:
范围是0~65535 (一个数字而已) 公认端口:0~1023,系统紧密绑定(binding)一些服务 注册端口(RegisterPorts):1024~49151 动态和/或私有端口(Dynamicand/orPrivatePorts):从49152到65535。 |
常用端口号:
21FTP 80HTTP 443 HTTPS |
3 UDP与TCP协议的区别:
TCP协议的特点(运行时先开服务端再开客户端,否则报错) | UDP协议的特点(程序运行时开接收端再开发送端,否则报错) |
1、面向连接 | 1、面向无连接 |
2、tcp基于IO流进行传输数据的,在连接中船输大量数据 | 2、基于数据包发送数据,每个包不能超过64Kb |
3、通过三次握手机制连接,可靠协议 | 3、面向无连接的,不可靠协议,会丢失数据 |
4、通讯前必须建立连接,效率稍低 | 4、面向无连接使用效率高 |
5、tcp通讯是分客户端与服务端 | 5、udp通讯分发送端与接收端 |
3.1 UDP(发送端)使用步骤
public class udpSenderDemo { public static void main(String[] args) throws IOException { //第一步:建立UDP的服务 DatagramSocket datagramSocket = new DatagramSocket(); //第二步:准备数据,把数据封装到数据包中 String data = "这个是我第一个UDP的例子"; DatagramPacket packet = new DatagramPacket(data.getBytes(), data.getBytes().length, InetAddress.getLocalHost(), 9090); //调用udp的服务发送数据包 datagramSocket.send(packet); //关闭资源 实际上就是释放端口号。 datagramSocket.close(); } } |
3.2 UDP(接收端)使用步骤
public class udpReceiveDemo { public static void main(String[] args) throws IOException { //第一步:建立udp的服务.并且要监听一个端口号。 DatagramSocket datagramSocket = new DatagramSocket(9090); //第二步:准备一个空的数据包接收数据 byte[] buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf, buf.length); //第三步:接收数据包 datagramSocket.receive(packet); // receive 是一个阻塞型的方法,如果没有接收到数据包之前,这个方法会一直等待下去。直到接收到数据包为止。 System.out.println("接收到的数据:"+ new String(buf,0,packet.getLength())); // packet.getLength() 返回数据包所接收到的字节数。 //第四步:关闭资源 datagramSocket.close(); } } |
3.3 TCP(服务端)使用步骤
public class tcpDemoServer { public static void main(String[] args) throws IOException { //第一步:建立tcp的服务端。 并且要监听一个端口。 ServerSocket serverSocket = new ServerSocket(9090); //第二步:接受客户端的连接。 Socket socket = serverSocket.accept(); // accept() 也是一个阻塞型的方法,如果没有客户端与其进行连接,那么会一直等待下去。 //获取输入流对象 InputStream inputStream = socket.getInputStream(); //读取数据 byte[] buf = new byte[1024]; int length = 0 ; length = inputStream.read(buf); System.out.println("服务端接受的数据:"+ new String(buf,0,length));
//服务端给客户端回送一条数据 OutputStream outputStream = socket.getOutputStream(); outputStream.write("客户端你好,我是服务端啊!".getBytes()); //关闭资源 serverSocket.close(); } } |
3.4 TCP(客户端)使用步骤
public class tcpDemoClient { public static void main(String[] args) throws IOException { //建立tcp客户端的服务 注意: tcp的客户端一旦建立服务,那么马上就要和服务端连接。 Socket socket = new Socket(InetAddress.getLocalHost(), 9090); //准备数据,把数据发出去 String data = "这个是我第一个tcp的例子!!"; //获取对应的输出流对象,把数据写出去。 OutputStream outputStream = socket.getOutputStream(); outputStream.write(data.getBytes()); //接收服务端回送的数据 InputStream inputStream = socket.getInputStream(); byte[] buf = new byte[1024]; int length = inputStream.read(buf); System.out.println("客户端接收到的数据:"+ new String(buf,0,length)); //关闭资源 socket.close(); } } |
4 疑问:
疑问:为什么ServerSocket不设计一个getInputStream或者getOuputStream方法?
为了防止ServerSocket产生多个getInputStream或者getOuputStream不知道如何处理。
ServerSocket没有获取流方法的原因
5.经典例子
5.1建立一个文本转换服务器
/* 需求:建立一个文本转换服务器。 客户端给服务端发送文本,服务单会将文本转成大写在返回给客户端。 而且客户度可以不断的进行文本转换。当客户端输入over时,转换结束。
*/
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket;
public class TransTextDemo {
}
//客户端 class TransClient{ public static void main(String[] args) throws IOException { Socket s =new Socket(InetAddress.getLocalHost(),9090); //定义读取键盘数据的流对象 BufferedReader bufr =new BufferedReader(new InputStreamReader(System.in));
/*//定义目的,将数据写到socket输出流,发给服务端 BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); */ //更方便的打印流,不用考虑转行流 PrintWriter out = new PrintWriter(s.getOutputStream(), true); //取代bufOut,第二个参数为自动换行
//定义一个Socket读取流,读取服务端返回的大写信息 BufferedReader bufIn = new BufferedReader(new InputStreamReader( s.getInputStream()));
String line =null; while((line =bufr.readLine())!=null){ if("over".equals(line)) break;
out.println(line); // bufOut.write(line+"\r\n"); // bufOut.flush();
String str =bufIn.readLine(); System.out.println("server:"+str); } bufr.close(); s.close(); } }
//服务端 class TransServer{ public static void main(String[] args) throws IOException { ServerSocket ss =new ServerSocket(9090); Socket s =ss.accept();
//读取socket读入流的数据 BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
// //读取socket输出流,将大写数据写到socket输出流,并发给客户端 // BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//更方便的打印流,不用考虑转行流 PrintWriter out = new PrintWriter(s.getOutputStream(), true); //取代bufOut,可以减去刷内存的数据,第二个参数为自动换行
String line =null; while((line =bufIn.readLine())!=null){ System.out.println(line);
out.println(line.toUpperCase()); // bufOut.write(line.toUpperCase()+"\r\n"); // bufOut.flush(); } ss.close(); } }
/* 该例子出现的问题。 现象:客户端和服务端都在莫名的等待。 为什么呢? 因为客户端和服务端都有阻塞式方法。这些方法么没有读到结束标记。那么就一直等 而导致两端,都在等待。 */ |
5.2客户端并发上传图片到服务端(多线程)
class PicClient{ public static void main(String[] args) throws Exception { /*if(args.length!=1) { System.out.println("请选择一个jpg格式的图片"); return ; } File file = new File(args[0]); if(!(file.exists() && file.isFile())) { System.out.println("该文件有问题,要么补存在,要么不是文件"); return ;
}
if(!file.getName().endsWith(".jpg")) { System.out.println("图片格式错误,请重新选择"); return ; }
if(file.length()>1024*1024*5) { System.out.println("文件过大,没安好心"); return ; }*/ Socket s =new Socket(InetAddress.getLocalHost(),9090); //建立一个文件输入流 File file =new File("D:\\1.jpg"); FileInputStream fis = new FileInputStream(file); OutputStream out = s.getOutputStream();
byte[] buf = new byte[1024]; int len =0; //边读边写 while((len=fis.read(buf))!=-1){ //读文件 out.write(buf, 0, len); }
//没有关闭输出流通道,会造成一直等待状态,图片会上传不成功 s.shutdownOutput();
InputStream in = s.getInputStream(); byte[] bufIn = new byte[1024]; int num =in.read(bufIn); System.out.println(new String(bufIn,0,num));
fis.close(); s.close();
} }
//创建一个线程实现类 class PicThread implements Runnable{ private Socket s; PicThread(Socket s) { this.s=s; } public void run() { int count =1; String ip = s.getInetAddress().getHostAddress(); try { System.out.println(ip+"...connected"); InputStream in = s.getInputStream();
//创建一个保存图片路径 File dir = new File("E:\\pic"); File file = new File(dir,ip+"("+count+")"+".jpg");//图片命名
//判断文件是否已经存在 while(file.exists()){ file = new File(dir,ip+"("+(count++)+")"+".jpg"); //避免前面的文件被覆盖 } FileOutputStream fos =new FileOutputStream(file);
//定义一个缓存数组 byte[] buf = new byte[1024]; int len=0; //图片边读边写 while((len=in.read(buf))!=-1){ fos.write(buf, 0, len); } //反馈服务器 OutputStream out =s.getOutputStream(); out.write("上传成功".getBytes()); //关闭资源,释放端口 fos.close(); s.close(); } catch (Exception e) { throw new RuntimeException(ip+"上传失败"); } } }
class PicServer{ public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(9090); while(true){ Socket s =ss.accept(); //接收到一个请求,就开启一个线程,又接着等待下一个请求 new Thread(new PicThread(s)).start(); } } } |
|
//需求:从服务端下载图片 class DownloadClient{ public static void main(String[] args) throws IOException { Socket socket =new Socket(InetAddress.getLocalHost(),9898); //获取一个socket输入流 InputStream in=socket.getInputStream(); //创建文件的输出流 FileOutputStream fos = new FileOutputStream("E:\\pic\\17岁.mp3");//目标路径 byte[] buf = new byte[1024]; int len =0; while((len=in.read(buf))!=-1){ fos.write(buf,0,len); } System.out.println("下载成功"); fos.close(); socket.close(); } }
class DownloadServer extends Thread{ Socket socket; public DownloadServer(Socket socket){ this.socket=socket; }
@Override public void run() { try { //写数据出去,获取socket输出流 OutputStream out = socket.getOutputStream(); //读取图片数据写到客户端 FileInputStream fis =new FileInputStream("E:\\音乐\\music\\17岁.mp3");//源路径 byte[] buf =new byte[1024]; int len=0; while((len=fis.read(buf))!=-1){ out.write(buf, 0, len); } //下载完,关闭输出流 socket.shutdownOutput(); fis.close(); }catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { ServerSocket serverSocket =new ServerSocket(9898); while(true){ //不断接收客户连接 Socket socket =serverSocket.accept(); //开启一个线程服务一个客户 new DownloadServer(socket).start(); } } } |
GUI
1容器组件
容器类 JOptionPan showMessageDialog(parentComponent,message,title,int messageType); parentComponent 所有者 message 显示消息 title 标题 messageType 对话框类型。消息、警告、错误...
文件对话框 FileDialog FileDialog(parent, title, mode) parent 所有者 title 标题 mode : 模式 FileDialog.LOAD 或 FileDialog.SAVE |
public class FramDemo1 { public static void main(String[] args) { //创建窗体对象 // JFrame frame = new JFrame("窗体");
//消息对话框 // JOptionPane.showMessageDialog(frame, "今天天气比较不错", "消息对话框", JOptionPane.INFORMATION_MESSAGE); // JOptionPane.showMessageDialog(frame, "你死定了", "警告", JOptionPane.WARNING_MESSAGE); //警告对话框 // JOptionPane.showMessageDialog(frame, "YOU OUT", "错误", JOptionPane.ERROR_MESSAGE);//错误对话框
//确定对话框 // int option = JOptionPane.showConfirmDialog(frame, "你要继续安装?"); // System.out.println("option:"+option);
//输入对话框 // String money = JOptionPane.showInputDialog(frame, "请等待"); // System.out.println("正则出钱"+money);
//文件对话框 /* FileDialog fileDialog = new FileDialog(frame, "请选择你要操作的文件", FileDialog.LOAD);//加载路径 FileDialog fileDialog = new FileDialog(frame, "请选择你要操作的文件", FileDialog.SAVE);//保存文件 fileDialog.setVisible(true);//设置文件可见性 System.out.println("文件路径:"+fileDialog.getDirectory()); System.out.println("文件名:"+fileDialog.getFile());*/
// 面板:JPanel JFrame frame = new JFrame("窗体");//创建窗体 JPanel panel =new JPanel();//创建面板对象 frame.add(panel); panel.setBackground(Color.blue); initFrame(frame, 300, 400); }
//设置窗口在屏幕的中央位置 public static void initFrame(JFrame frame,int width,int height){ Toolkit toolkit =Toolkit.getDefaultToolkit();获取到了默认的工具包类,这个工具包类可以帮我们获取系统的信息。 Dimension dimension = toolkit.getScreenSize();//获取屏幕的大小 int x= (int) ((dimension.getWidth()-width)/2); int y = (int) ((dimension.getHeight()-height)/2); frame.setBounds(x, y, width, height); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } |
疑问:为什么java.awt 包有的图形类,在javax。swing包里面也一样存在呢,javax。swing包里面的图形类只是在类名之前加上了J开头。
java.awt包画出的图形都是依赖于系统的图形库。
javax.swing 都是sun自己去实现的图形,不需要借助系统的图形库。
疑问:现在既然推荐我们使用javax.swing包里面的图形类,那为什么不删除了awt包里面的图形类呢?
1.兼顾到新老系统的原因
2.awt包里面还有其他Event等其他类
2布局管理器
2.1 BorderLayout (边框布局管理器)
边框布局管理器要注意的事项:
1. 如果使用了边框布局管理器,添加组件的时候没有指定组件的添加方位,那么默认是在中间位置。
2. 使用了边框布局管理器,如果那个东南西北那个方位没有组件,那么中间的组件就是占据空缺的位置。
3. 窗体默认的布局管理器就是BorderLayout。
public class Demo1 {
public static void main(String[] args) { JFrame frame = new JFrame("边框布局管理器"); //创建一个边框布局管理器 /* BorderLayout borderLayout = new BorderLayout(); //设置窗体使用边框布局管理器 frame.setLayout(borderLayout);*/
frame.add(new JButton("北"),BorderLayout.NORTH); frame.add(new JButton("南"),BorderLayout.SOUTH); frame.add(new JButton("西"),BorderLayout.WEST); frame.add(new JButton("东"),BorderLayout.EAST); frame.add(new JButton("中"),BorderLayout.CENTER); FrameUtil.initFrame(frame, 300, 400); } } |
2.2 FlowLayout(流式布局管理器)
流式布局管理器要注意的事项:
1. 流式布局管理器默认是居中对齐。
2. 面板的默认布局管理器是 FlowLayout
public class Demo2 {
public static void main(String[] args) { //创建一个窗体对象 JFrame frame = new JFrame("窗体"); //创建一个面板对象 JPanel panel = new JPanel(); //创建一个流式布局管理器 FlowLayout flowLayout = new FlowLayout(FlowLayout.LEFT,0,20); // 第一个参数:对齐方式, 第二个参数: 水平间隙, 第三个:垂直间隙 //设置面板使用流式布局管理器 panel.setLayout(flowLayout); panel.add(new JButton("1")); panel.add(new JButton("2")); panel.add(new JButton("3")); panel.add(new JButton("4")); panel.add(new JButton("5")); frame.add(panel); FrameUtil.initFrame(frame, 300, 300); } } |
2.3 (GridLayout)网格布局管理器
注意:如果指定的网格数量不够存储现有的组件,那么就会多增加一列。
public class Demo3 {
public static void main(String[] args) { JFrame frame = new JFrame("网格布局管理器"); //创建一个网格布局管理器 GridLayout gridLayout = new GridLayout(4, 4); //设置窗体为网格布局管理器 frame.setLayout(gridLayout);
//添加了10组件 for(int i = 0 ; i< 10 ; i++){ frame.add(new JButton(i+"")); } frame.add(new JButton(".")); frame.add(new JButton("+")); frame.add(new JButton("-")); frame.add(new JButton("*")); frame.add(new JButton("/")); frame.add(new JButton("=")); frame.add(new JButton("哈哈")); FrameUtil.initFrame(frame, 300, 300); } } |
2.4 CardLayout (卡片布局管理器)
public class Demo4 {
public static void main(String[] args) { final JFrame frame = new JFrame("卡片布局管理器"); final JPanel panel = new JPanel(); //创建一个卡片布局管理器 final CardLayout cardLayout = new CardLayout(); panel.setLayout(cardLayout); //添加组件 JButton button = new JButton("黑桃A"); panel.add(button); //注册了一个事件 button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { cardLayout.next(panel); } });
panel.add(new JButton("红桃K")); panel.add(new JButton("梅花六")); panel.add(new JButton("方块2"));
frame.add(panel); FrameUtil.initFrame(frame, 300, 300); } } |
3事件
鼠标监听器
public class Demo2 {
public static void main(String[] args) { JFrame frame = new JFrame("鼠标监听器"); JButton button = new JButton("按钮"); frame.add(button); /* //添加鼠标监听器 button.addMouseListener(new MouseListener() {
@Override public void mouseReleased(MouseEvent e) { System.out.println("鼠标松开..."); }
@Override public void mousePressed(MouseEvent e) { System.out.println("鼠标按下..."); }
@Override public void mouseExited(MouseEvent e) { System.out.println("鼠标移出 ...."); }
@Override public void mouseEntered(MouseEvent e) { System.out.println("鼠标移入 ..."); }
@Override public void mouseClicked(MouseEvent e) { System.out.println("鼠标单击.."); } });*/ //鼠标适配器 MouseAdapter 是实现了MouseListener 中的所有方法的。但是全部都是空实现。
button.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if(e.getClickCount()==2){ System.out.println("双击了.."); } } }); //初始化窗体 FrameUtil.initFrame(frame, 300, 300); } } |
批处理文件(bat)
jar 文件双击运行只对于图形化界面的程序起作用,对控制台程序是不起作用的。
bat文件(批处理文件): 批处理文件的意思就是一次性可以执行多个命令. 只需要把要执行的命名编写bat文件即可。 bat作用:可以快速启动一个项目(图形化界面的程序以及控制台的程序) 如何编写ba处理文件:新建一个记事本,把后缀名改成bat即可。
bat处理文件常见的命令: pause : 让当前控制台停留。 echo off 表示在此语句后所有运行的命令都不显示命令行本身 @ 隐藏当前行执行过命名。 echo 表示显示此命令后的字符 tiltle 设置窗口的标题。 color 设置窗体的字体颜色。 rem 表示此命令后的字符为解释行(注释),不执行 %[1-9]表示参数,参数是指在运行批处理文件时在文件名后加的以空格(或者Tab)分隔的字符串 |
对象拷贝
对象克隆要注意的细节: 1. 如果一个对象所属的类没有去实现Cloneable接口,那么该对象不能被克隆。 2. Cloneable 这个接口是没有任何的方法的,是一个标识接口。 3. 对象的克隆是不会调用构造函数的。 4. 对象的浅克隆,如果一个对象中维护了另外一个对象的引用,那么使用对象的浅克隆只是克隆另外一个对象的地址而已,并没有把另外一个对象也克隆了一份。 注意:调用clone的方法克隆一个对象的时候,这种方式是属于对象的浅克隆.
对象的深克隆: 对象的深克隆其实就是借用了对象的输入输出流实现。
|
实现的代码
class Address implements Serializable{ //实现序列化接口,把对象写到流里的过程是串行化(Serilization)过程 String country; String city;
public Address(String country, String city) { super(); this.country = country; this.city = city; } }
class Person implements Cloneable,Serializable{
int id; int id;
String name;
Address address;
public Person(int id, String name) { this.id = id; this.name = name; }
public Person(int id, String name, Address address) { this.id = id; this.name = name; this.address = address; }
@Override public String toString() { return "编号:"+ this.id+" 姓名:"+ this.name+ " 地址:"+this.address.city ; }
@Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
public class Demo1 {
public static void main(String[] args) throws CloneNotSupportedException { //创建一个地址对象 Address address = new Address("中国", "广州");
Person p1 = new Person(110,"狗娃",address); Person p2 = (Person) p1.clone(); //内存地址的引用而已。不属于对象的克隆。
p2.address.city = "北京"; System.out.println("p1:"+ p1); // 北京 System.out.println("p2:"+ p2); // 北京 } } |
public class Demo2 {
public static void main(String[] args) throws Exception { Address address = new Address("中国", "广州"); Person p1 = new Person(110,"狗娃",address); // writeObj(p1); //首先写到文件 Person p2 = (Person) redObj(); p2.address.city = "北京"; //深克隆,修改P2的属性值,P1的不会受影响 System.out.println("p1:"+ p1); System.out.println("p2:"+ p2); }
//第二步:读取文件中的对象 public static Object redObj() throws Exception{ //找到目标文件 File file = new File("F:\\obj.txt"); //建立对象的输入流 FileInputStream fileInputStream = new FileInputStream(file); //建立对象的输入流通道 ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); //读取对象 return objectInputStream.readObject(); }
//首先先要把对象写入到文件上。 public static void writeObj(Person p1) throws IOException{ //创建一个文件对象 File file = new File("f:\\obj.txt"); //创建对象对象输出流对象 FileOutputStream fileOutputStream = new FileOutputStream(file); ObjectOutputStream objectOutputStream= new ObjectOutputStream(fileOutputStream); //把对象写出去 objectOutputStream.writeObject(p1); //关闭流 fileOutputStream.close(); } }
/*运行结果 p1:编号:110 姓名:狗娃地址:广州 p2:编号:110 姓名:狗娃地址:北京 */ |
总结:浅克隆只是复制了对象的引用地址,深克隆可以理解为把对象及其内存地址都写到硬盘上。
1.反射
概念:
反射: 一个class文件加载到内存的时候,那么jvm就会马上对这个class文件进行解剖,得到这个类的所有的信息,然后jvm会马上创建一个Class对象,把这个类的所有信息封装到Class对象上。我们只需要拿到了这个Class对象那么我们就可以操作这个类的任意成员。包括 构造方法、成员属性、成员函数....
注意:在反射里面一个类的任何成员都会对应的类描述。 |
1.1三种方式加载类的字节码
public class Demo1 { Person p; public static void main(String[] args) throws ClassNotFoundException { //方式1: Class.forName(类文件的完整路径) Class clazz1 = Class.forName("cn.itcast.reflect.Person");
//方式2:通过类名直接获取 Class clazz2 = Person.class; System.out.println("clazz1==clazz2?"+ (clazz1==clazz2));
//方式3:通过对象获取 Person p = new Person(); Class clazz3 = p.getClass(); System.out.println("clazz2==clazz3?"+ (clazz2==clazz3));//true } } |
1.2通过字节码对象获取构造方法。
使用构造方法创建对象。
1.getName()类的名称(全名,全限定名) 2 getSimpleName()类的的简单名称(不带包名) 3. getModifiers(); 类的的修饰符 4.创建对象 无参数构造创建对象 newInstance() 5. 获取指定参数的构造器对象,并可以使用Constructor对象创建一个实例 Constructor<T> getConstructor(Class<?>... parameterTypes) |
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, Exception, SecurityException { //获取对应的字节码对象 Class clazz = Class.forName("cn.itcast.reflect.Person"); //获取所有构造方法 /* Constructor[] constructors = clazz.getConstructors(); //getConstructors 只是获取到了公共的构造方法。 Constructor[] constructors = clazz.getDeclaredConstructors() ; // 公共、私有的构造方法都可以获取出来。 for(Constructor constructor : constructors){ System.out.println(constructor); }
Constructor constructor = clazz.getConstructor(int.class,String.class); //创建一个对象。 Person p = (Person) constructor.newInstance(110,"狗娃"); System.out.println(p); */
//通过反射获取私有的构造方法 Constructor constructor = clazz.getDeclaredConstructor(int.class); //设置访问权限 . 暴力反射 constructor.setAccessible(true); //使用私有的构造方法创建对象 Person p2 = (Person) constructor.newInstance(119); //返回是Constructor类型,所以要强转 System.out.println(p2); } } |
1.3通过字节码(Class对象)获取对应的方法。
sun也使用了一个类描述了方法----Method类
1.获取公共方法包括继承的父类的方法 getMethods()返回一个数组,元素类型是Method 2.获取指定参数的公共方法 getMethod("setName", String.class); 3.获得所有的方法,包括私有 Method[] getDeclaredMethods() 4.获得指定参数的方法,包括私有 Method getDeclaredMethod(String name, Class<?>... parameterTypes) |
public class Demo3 {
public static void main(String[] args) throws Exception { //获取对应的字节码对象 Class clazz = Class.forName("cn.itcast.reflect.Person"); //获取所有的方法 /* Method[] methods = clazz.getMethods(); //获取所有公共的方法,包括了父类的的方法。 Method[] methods = clazz.getDeclaredMethods(); //获取本类的公共、非公共的所有方法。注意:是不包括父类的构造的方法的 for(Method m : methods){ //遍历所以方法并输出 System.out.println(m); } */ Person p= new Person(119,"小明");
/*获取指定的方法 Method m = clazz.getMethod("eat",int.class) ; // 第一个参数:方法名、第二参数:形参的列表 Object message = m.invoke(p, 4); //第一个参数:执行这个方法的对象,第二个参数:方法需要的参数。 System.out.println("返回值:"+message); */
//获取私有的方法 Method m = clazz.getDeclaredMethod("sum",int[].class); //暴力反射 m.setAccessible(true); //获取权限(实际上只是取消检查而已) //执行方法 int[] arr = {12,1,2,3}; m.invoke(p, arr); } } |
1.4通过字节码对象获取成员变量。
Field 类描述了成员变量。
1.获取公共字段 Field[] getFields() 2.获取指定参数的公共字段 Field getField(String name) 3.获取所有的字段 Field[] getDeclaredFields() 4.获取指定参数的字段,包括私用 Field getDeclaredField(String name) |
public class Demo4 {
public static void main(String[] args) throws Exception { //获取对应的字节码对象 Class clazz = Class.forName("cn.itcast.reflect.Person"); //通过字节码对象获取所有的成员变量 //Field[] fields = clazz.getFields(); /*Field[] fields = clazz.getDeclaredFields(); for(Field f : fields){ System.out.println(f); }*/
//获取单个 Person p = new Person(); Field field = clazz.getDeclaredField("id"); field.setAccessible(true); field.setInt(p, 180); //第一个参数:对象 。 第二个参数:属性值。 System.out.println(p); } } |
暴力反射
public void setAccessible(boolean flag)
throws SecurityException
值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施Java 语言访问检查。(暴力反射只是取消权限的检查,而不是修改其权限修饰符)
2工厂设计模式
工厂设计模式:工厂设计模式就是定义一个方法专门用于生产对象的就称作为工厂设计模式。
class Car{} class BMW extends Car{} class BSJ extends Car{} public class Demo1 {
public static void main(String[] args) throws Exception { Emp p = (Emp) getInstance(); System.out.println(p); }
//需求:编写一个工厂方法用于产生任意类型的对象。具体产生的对象是根据配置文件而定。 public static Object getInstance() throws Exception{ //找到目标文件 File file = new File("obj.txt"); //建立对应的输入流对象 BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); //获取对应的class文件名 String className = bufferedReader.readLine();
//第一步:获取到这个类的字节码对象、 Class clazz = Class.forName(className); //先获取到构造方法。无参的构造方法 Constructor constructor = clazz.getDeclaredConstructor(null); //创建对象。 Object o = constructor.newInstance(null); //无参构造方法 //设置成员变量到对象中。 String line = null; while((line = bufferedReader.readLine())!=null){ String[] datas = line.split("="); //得到了对应的成员属性 Field field = clazz.getDeclaredField(datas[0]); field.setAccessible(true); //给对象设置属性值了 if(field.getType()==int.class){ field.set(o, Integer.parseInt(datas[1])); }else{
field.set(o, datas[1]); } } return o; } } |