第七、八章
-
当DatagramSocket接收到了来自任意一个主机的数据报,如何知道该数据报的发送者的UDP端口?(A)
A 调用DatagramPacket的getPort()方法
B 调用DatagramSocket的getPort()方法
C 调用DatagramPacket的getRemotePort()方法
D 调用DatagramSocket的getRemotePort()方法(1)DatagramSocket的所有构造方法都只处理本地地址和端口,远程地址和端口存储在DatagramPacket中,与DatagramSocket无关。
(2)DatagramPacket类包括以下属性:
data:表示数据报的数据缓冲区。
offset:表示数据报的数据缓冲区的起始位置。
length:表示数据报的长度。
address:对于用于发送的数据报,address属性表示数据报的目标地址。对于用于接收的数据报,在收到数据后,address属性表示数据报的源地址。
port:对于用于发送的数据报,port属性表示数据报的目标UDP端口。对于用于接收的数据报,在收到数据报后,port属性表示数据报的源UDP端口。
(3)扩展:如果想知道一个DatagramSocket对象所绑定的本地地址,可以调用它的以下方法:
int getLocalPort():返回DatagramSocket所绑定的端口。
InetAddress getLocalAddress():返回DatagramSocket所绑定的IP地址。
SocketAddress getLocalSocketAddress():返回一个SocketAddress对象,它包含DatagramSocket所绑定的IP地址和端口信息。 -
MulticastSocket的哪些方法要求MulticastSocket已经加入到主播组?(B)
A send()方法
B receive()方法
C leaveGroup()方法
D bind()方法MulticastSocket支持以下四种操作:
(1)加入到组播组:joinGroup()方法
(2)向组中成员发送数据报:send()方法
(3)接收发送到组播组的数据报:receive()方法
(4)离开组播组:leaveGroup()方法
注意:
必须在加入组播组后,才能接收发往所加入的组播组的组播数据,向组播组发送时数并不需要加入组播组。就是说必须加入组播组后,才能接收发往组播组的组播数据。 -
以下代码
System.out.println(new ServerSocket().getReuseAddress()); System.out.println(new Socket().getReuseAddress()); System.out.println(new DatagramSocket().getReuseAddress()); System.out.println(new MulticastSocket().getReuseAddress());
在不抛出异常的情况,输出结果为:(C)
A true, true, fasle, fasle
B false, true, false, true
C fasle, false, false, true
D true, false, false, false -
关于MulticastSocket对象,以下说法正确的是(A)
A 接收组播数据必须先加入组播组,发送组播数据不须加入组播组
B 接收组播数据不须先加入组播组,发送组播数据必须加入组播组
C 接收组播数据必须先加入组播组,发送组播数据也必须加入组播组
D 接收组播数据不须先加入组播组,发送组播数据也不须加入组播组必须在加入组播组后才能接收发往所加入的组播组的组播数据,向组播组发送时数并不需要加入组播组。
-
设socket是一个DatagramSocket对象,packet是一个DatagramPacket对象,在执行socket.send(packet);来发送数据报时,待发送数据报的目的地址存放在____里,在执行socket.receive(packet);来接收数据报后,收到的数据报的源地址存放在_____ 里。(B)
A socket, socket
B packet, packet
C socket, packet
D packet, socket解析:DatagramPacket与DatagramSocket区别
在码头发送和接收货物时都需要使用集装箱来装载货物,UDP通信也是一样,发送和接收的数据也需要使用“集装箱”进行打包,为此JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据。然而运输货物只有“集装箱”是不够的,还需要有码头。在程序中需要实现通信只有DatagramPacket数据包也同样不行,为此JDK中提供的一个DatagramSocket类。DatagramSocket类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包,发送数据的过程如下图所示。
数据包存放在Packet中。 UDP通信中,需要建立一个DatagramSocket,与Socket不同,它不存在"连接"的概念,取而代之的是一个数据报包–DatagramPacket。这个数据报包必须知道自己来自何处,以及打算去哪里。所以本身必须包含IP地址、端口号和数据内容。 -
以下说法正确的是:______。(B)
A 使用同一个Socket对象可以接收来自不同源地址数据
B 使用同一个DatagramSocket对象可以向不同的目的地址发送数据
C 使用同一个Socket对象可以向不同的目的地址发送数据
D 使用同一个DatagramSocket对象不可以接收来自不同源地址的数据Socket的工作原理:
一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。
服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
DatagramSocket支持一对一,一对多,多对一,多对多
Socket只支持一对一,B选项正确; -
关于组播组与MulticastSocket以下说法正确的是(D)
A 一个MulticastSocket对象只能加入一个组播组,同一主机上的一个组播组只能被一个MulticastSocket对象加入。
B 一个MulticastSocket对象可以加入多个组播组,同一主机上的一个组播组只能被一个MulticastSocket对象加入。
C 一个MulticastSocket对象只能加入一个组播组,同一主机上的一个组播组可以被多个MulticastSocket对象加入
D 一个MulticastSocket对象可以加入多个组播组,同一主机上的一个组播组可以被多个MulticastSocket对象加入。一个MulticastSocket可以同时加入多个组播组,这时它会收到发往所加入的任意一个组播组的播组数据;
多个MulticastSocket可以加入同一个组播组。 -
关于DatagramChannel的read(ByteBuffer[] srcs)方法,下面哪些说法是正确的?(ACD)
A read()方法要求DatagramChannel已经建立连接。
B 如果ByteBuffer缓冲区无法容纳数据报中的所有数据,那么read()方法会抛出BufferOverflowException。
C 当DatagramChannel工作于非阻塞模式,并且没有就绪的数据报,read()方法立即返回0。
D 当DatagramChannel工作于阻塞模式,并且没有就绪的数据报,read()方法会进入阻塞状态。DatagramChannel的read()
read()方法要求DatagramChannel已经建立连接而receive()方法则没有这一限制;
以下情况下read()方法直接返回0
(1)DatagreamChannel工作于非阻塞模式,并且没有数据报到达
(2)接受到的数据报中不包含数据
(3)ByteBuffer缓冲区已满
如果Bytebuffer缓冲区无法容纳数据报中的所有数据,多余数据将被丢弃,且不抛出异常
扩展:
DatagramChannel的write()与send()方法的区别在于:
write()方法要求DatagramChannel已经建立连接
在非阻塞模式下,write()方法不保证把ByteBuffer内的所有剩余数据作为一个数据报发送。
假如ByteBuffer的剩余数据为r,实际发送的字节数为n,那么 0<=n<=r。而send()方法总是把ByteBuffer内的所有剩余数据作为一个数据报发送
在非阻塞模式下,如果要通过write()方法发送buffer内的所有剩余数据,可以采用以下方式:
while(buffer.hasremaining())&&channel.write(buffer)!=-1) -
关于DatagramSocket的connect()和disconnect()方法,下面哪些说法是正确?(AC)
A connect()方法使得DatagramSocket暂时只能与特定的远程DatagramSocket通信。
B DatagramSocket的connect()方法与Socket的connect()方法的作用相同。
C 可以对一个DatagramSocket对象多次调用connect()方法。
D 调用了DatagramSocket的disconnect()方法后,这个DatagramSocket就失效了,不能再收发数据报。利用以下方法,可使一个DatagramSocket只能与另一个固定的DatagramSocket交换数据报:
public void connect(InetAddress host,int port)
public void disconnect()
public int getPort() //未建立时“连接”返回-1
public InetAddress getInetAddress() //未建立时“连接”返回null
publicSocketAddress getRemoteSocketAddress() //未建立时“连接”返回null
可以反复连接和断开连接。 -
以下哪些叙述是正确的?(BC)
A DatagramSocket的send(DatagramPacket src)方法发送数据报时,如果无法送达接收方,该方法会抛出IOException。
B UDP协议是无连接的协议。
C 对于用于接收数据的DatagramPacket,如果实际接收到的数据报的长度大于DatagramPacket的长度,那么多余的数据就会被丢弃。
D DatagramSocket的getInputStream()方法用于获得输入流。A 选项,DatagramSocket是无连接的,对方没有接收到不会抛出异常。
D 选项,DatagramSocket没有输入输出流。 -
关于DatagramSocket的选项,下面哪些说法是正确的?(BD)
A SO_TIMEOUT选项表示接收数据报以及发送数据报时的等待超时时间。
B 许多网络都都限定了接收缓冲区大小的最大值,如果DatagramSocket的setReceiveBufferSize(int size)方法的参数size超过该值,那么setReceiveBufferSize(int size)方法所作的设置无效。
C UDP Socket和TCP Socket的SO_REUSEADDR选项的作用相同。
D SO_BROADCAST选项决定是否允许对网络广播地址收发数据报。DatagramSocket有以下选项:
SO_TIMEOUT:表示接收数据报时的等待超时时间,必须在调用receive()之前设置
SO_RCVBUF:表示接收数据的缓冲区的大小
SO_SNDBUF:表示发送数据的缓冲区的大小
SO_REUSEADDR:表示是否允许多个DatagramSocket所绑定相同的地址,必须在DatagramSocket绑定在端口之前设置
SO_BROADCAST:表示是否允许对网络广播地址收发数据报(默认值为true) -
以下是DatagramPacket的构造方法,哪些用于发送数据报?(CD)
A public DatagramPacket(byte[] data,int length)
B public DatagramPacket(byte[] data,int offset, int length)
C public DatagramPacket(byte[] data,int offset, int length,InetAddress address,int port)
D public DatagramPacket(byte[] data,int offset,int length,SocketAddress address)用于接收数据的构造方法包括:
public DatagramPacket(byte[] data, int offset, int length, InetAddress address, int port)
public DatagramPacket(byte[] data, int offset, int length, SocketAddress address)
public DatagramPacket(byte[] data, int length, InetAddress address,int port)
public DatagramPacket(byte[] data,int length,SocketAddress address)
小结:
- DatagramSocket负责接收和发送UDP数据报,DatagramPacket表示UDP数据报
- UDP协议是无连接的协议,客户端的DatagramSocket与服务器端的DatagramSocket不存在一一对应关系,两者无需建立连接,就能交换数据报。
关于数据报的所有信息(包括目标地址和目的端口)都包含在DatagramPacket中,DatagramSocket只需要了解在本地的哪个端口上监听或发送 - java.net.MulticastSocket具有组播的功能,它是DatagramSocket的子类
和DatagramSocket一样,MulticastSocket也与DatagramPacket搭配使用,DatagramPacket用来存放接收和发送的组播数据报。 - 同一台主机上的MulticastSocket对象不能与DatagramSocket对象占用相同的UDP端口。
第九章
-
对于一个类的两个不同版本,如果它们的序列化版本号serialVersionUID相同,就一定序列化兼容,这句话是否正确?(B)
A 正确
B 不正确类的serialVersionUID的默认值依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。
为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显式的定义serialVersionUID,为它赋予明确的值。
显式的定义serialVersionUID有两种用途:
(1)在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID。
(2)在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。 -
以下哪个类没有实现java.io.Serializable接口?(A)
A java.io.OutputStream类
B java.lang.String类
C java.lang.Integer类
D java.util.Date类只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则ObjectOutputStream的writeObject(Object obj)方法会抛出IOException。
实现Serializable或Externalizable接口的类也称为可序列化类。
Externalizable接口继承自Serializable接口
实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式。
JDK类库中的部分类(如String类、基本数据类型包装类<Integer, Boolean, Character,……>和Date类等)都实现了Serializable接口。 -
可序列化类中的writeObject()方法和readObject()方法使用什么访问控制修饰符?(D)
A public
B 没有
C protected
D private如果用户希望控制类的序列化方式,可以在可序列化类中c重写以下形式的writeObject()方法和readObject()方法:
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; -
以下哪一段代码向C:\data.obj文件中写入一个Date对象?(B)
A ObjectOutputStream out=new ObjectOutputStream(new fileOutputStream(“C:\data.obj”));
out.writeDate(new Date());
B ObjectOutputStream out=new ObjectOutputStream(new fileOutputStream(“C:\data.obj”));
out.writeObject(new Date());
C FileOutputStream out=new FileOutputStream(“C:\data.obj”);
out.writeObject(new Date());
D FileOutputStream out=new FileOutputStream(new ObjectOutputStream"C:\data.obj"));
out.writeObject(new Date());对象的序列化主要步骤:
(1)创建一个对象输出流,它可以包装一个其他类型的目标输出流,比如文件输出流:
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(“D:\objectFile.obj”));
(2)通过对象输出流的writeObject()方法写对象:
out.writeObject(“hello”); //写一个String对象
out.writeObject(new Date()); //写一个Date对象 -
默认的序列化方式有什么特点?(AB)
A 仅仅对对象的非transient的实例变量进行序列化
B 不会序列化对象的transient的实例变量
C 会序列化静态变量
D 会序列化对象的所有public类型的成员变量如果一个实例变量被transient修饰符修饰,那么默认的序列化方式不会对它序列化。 根据这一特点,可以用transient修饰符来修饰以下类型的实例变量:
(1)实例变量不代表对象的固有的内部数据,仅仅代表具有一定逻辑含义的临时数据。
(2)实例变量表示一些比较敏感的信息(比如银行账户的口令),出于安全方面的原因,不希望对其序列化。
(3)实例变量需要按照用户自定义的方式序列化,比如经过加密后再序列化。
在这种情况下,可以把实例变量定义为transient类型,然后在writeObject()方法中对其序列化。
与默认反序列化方式的不同:在对实现了Externalizable接口的类的对象进行反序列化时,会先调用类的不带参数的构造方法。类如果实现了Externalizable接口,那么它必须具有public类型的不带参数的构造方法,否则这个类无法反序列化。 -
关于Externalizable接口,下面哪些说法正确?(ABCDF)
A Externalizable接口继承自Serializable接口。
B writeExternal()方法负责序列化操作。
C readExternal()方法负责反序列化操作。
D在对实现了Externalizable接口的类的对象进行反序列化时,一定会先调用类的不带参数的构造方法。
E 在对实现了Externalizable接口的类的对象进行反序列化时,一定会先调用类的静态代码块。
F 一个类如果实现了Externalizable接口,那么它必须具有public类型的不带参数的构造方法。Externalizable接口继承自Serializable接口。
如果一个类实现了Externalizable接口,那么将完全由这个类控制自身的序列化行为。
Externalizable接口中声明了两个方法:
public void writeExternal(ObjectOutput out)throws IOException
public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException
与默认反序列化方式的不同:**在对实现了Externalizable接口的类的对象进行反序列化时,会先调用类的不带参数的构造方法。**类如果实现了Externalizable接口,那么它必须具有public类型的不带参数的构造方法,否则这个类无法反序列化。 -
默认的反序列化方式有什么特点?(AC)
A 可能会调用类的静态代码块。
B 一定会调用类的静态代码块。
C 不会调用类的任何构造方法。
D 会调用类的不带参数的构造方法。默认方式反序列化时,有以下特点:
如果在内存中对象所属的类还没有被加载,那么会先加载并初始化这个类。如果在classpath中不存在相应的类文件,那么会抛出ClassNotFoundException。
在反序列化时不会调用类的任何构造方法。
小结:
- 把Java对象转换为字节序列的过程称为对象的序列化;把字节序列恢复为Java对象的过程称为对象的反序列化。
- ObjectOuputStream只能对实现了Serializable接口的类的对象进行序列化。
默认情况下,ObjectOuputStream按照默认方式序列化
仅对对象的非transient的实例变量进行序列化
不会序列化对象的transient的实例变量
不会序列化静态变量。 - 只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则ObjectOutputStream的writeObject(Object obj)方法会抛出IOException。