IP地址和端口号
1)IP地址
用来标志网络中的一个通信实体的地址。通信实体可以是计算机,路由器等。
2)IP地址分类
IPV4:32位地址,以点分十进制表示,如192.168.0.1
IPV6:128位(16个字节)写成8个16位的无符号整数,每个整数用四个十六进制位表示,数之间用冒号(:)分开,如:
3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
3)特殊的IP地址
127.0.0.1 本机地址
192.168.0.0--192.168.255.255私有地址,属于非注册地址,专门为组织机构内部使用。
4)端口:port
IP地址用来标志一台计算机,但是一台计算机上可能提供多种应用程序,使用端口来区分这些应用程序。 端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机上运行多个网络应用程序。 端口范围0---65535,16位整数
5)端口分类
公认端口 0—1023 比如80端口分配给WWW,21端口分配给FTP,22端口分配给SSH,23端口分配给telnet,25端口分配给smtp
注册端口 1024—49151 分配给用户进程或应用程序
动态/私有端口 49152--65535
6)理解IP和端口的关系
IP地址好比每个人的地址(门牌号),端口好比是房间号。必须同时指定IP地址和端口号才能够正确的发送数据
IP地址好比为电话号码,而端口号就好比为分机号。
介绍OSI七层模型和TCP/IP模型
OSI(Open System Interconnection),开放式系统互联参考模型 。是一个逻辑上的定义,一个规范,它把网络协议从逻辑上分为了7层。每一层都有相关、相对应的物理设备,比如常规的路由器是三层交换设备,常规的交换机是二层交换设备。OSI七层模型是一种框架性的设计方法,建立七层模型的主要目的是为解决异种网络互连时所遇到的兼容性问题,其最主要的功能就是帮助不同类型的主机实现数据传输。它的最大优点是将服务、接口和协议这三个概念明确地区分开来,通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯。
TCP/IP协议是Internet最基本的协议、Internet国际互联网络的基础,主要由网络层的IP协议和传输层的TCP协议组成。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。
ISO制定的OSI参考模型的过于庞大、复杂招致了许多批评。伴随着互联网的流行,其本身所采用的TCP/IP协议栈获得了更为广泛的应用和认可。在TCP/IP参考模型中,去掉了OSI参考模型中的会话层和表示层(这两层的功能被合并到应用层实现)。同时将OSI参考模型中的数据链路层和物理层合并为主机到网络层。
TCP协议和UDP协议的比较
TCP和UDP是TCP/IP协议栈中传输层的两个协议,它们使用IP路由功能把数据包发送到目的地,从而为应用程序及应用层协议(包括:HTTP、SMTP、SNMP、FTP和Telnet)提供网络服务。
TCP的server和client之间通信就好比两个人打电话,需要互相知道对方的电话号码,然后开始对话。所以在两者的连接过程中间需要指定端口和地址。
UDP的server和client之间的通信就像两个人互相发信。我只需要知道对方的地址,然后就发信过去。对方是否收到我不知道,也不需要专门对口令似的来建立连接。具体区别如下:
1)TCP是面向连接的传输。UDP是无连接的传输
2)TCP有流量控制、拥塞控制,检验数据数据按序到达,而UDP则相反。
3)TCP的路由选择只发生在建立连接的时候,而UDP的每个报文都要进行路由选择
4)TCP是可靠性传输,他的可靠性是由超时重发机制实现的,而UDP则是不可靠传输
5)UDP因为少了很多控制信息,所以传输速度比TCP速度快
6)TCP适合用于传输大量数据,UDP适合用于传输小量数据
更多Java进阶学习资料、2022大厂面试真题,完整资料已经给大家打包完毕,需要资料的小伙伴 可以点击此链接添加备注暗号进行免费领取
什么是Socket编程
Socket编程的定义如下:
所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则是使用套接字来进行分离。
套接字就像是传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者接收远程发来的数据;而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,是不知道也不需要知道的,也不会关心它如何传输,这属于网络其它层次的工作。
Socket实际是传输层供给应用层的编程接口。传输层则在网络层的基础上提供进程到进程问的逻辑通道,而应用层的进程则利用传输层向另一台主机的某一进程通信。Socket就是应用层与传输层之间的桥梁
使用Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。
生活案例1如果你想写封邮件发给远方的朋友,如何写信、将信打包,属于应用层。信怎么写,怎么打包完全由我们做主;而当我们将信投入邮筒时,邮筒的那个口就是套接字,在进入套接字之后,就是传输层、网络层等(邮局、公路交管或者航线等)其它层次的工作了。我们从来不会去关心信是如何从西安发往北京的,我们只知道写好了投入邮筒就OK了。
生活案例2:可以把Socket比作是一个港口码头,应用程序只要将数据交给Socket,就算完成了数据的发送,具体细节由Socket来完成,细节不必了解。同理,对于接收方,应用程序也要创建一个码头,等待数据的到达,并获取数据。
简述基于TCP和UDP的Socket编程的主要步骤
Java分别为TCP和UDP 两种通信协议提供了相应的Socket编程类,这些类存放在java.net包中。与TCP对应的是服务器的ServerSocket和客户端的Socket,与UDP对应的是DatagramSocket。
基于TCP创建的套接字可以叫做流套接字,服务器端相当于一个监听器,用来监听端口。 服务器与客服端之间的通讯都是输入输出流来实现的。基于UDP的套接字就是数据报套接字,• 两个都要先构造好相应的数据包。
基于TCP协议的Socket编程的主要步骤
服务器端(server):
1. 构建一个ServerSocket实例,指定本地的端口。这个socket就是用来监听指定端口的连接请求的。
2. 重复如下几个步骤:
a. 调用socket的accept()方法来获得下面客户端的连接请求。通过accept()方法返回的socket实例,建立了一个和客户端的新连接。
b. 通过这个返回的socket实例获取InputStream和OutputStream,可以通过这两个stream来分别读和写数据。
c. 结束的时候调用socket实例的close()方法关闭socket连接。
客户端(client):
1.构建Socket实例,通过指定的远程服务器地址和端口来建立连接。
2.通过Socket实例包含的InputStream和OutputStream来进行数据的读写。
3.操作结束后调用socket实例的close方法,关闭。
UDP
服务器端(server):
1. 构造DatagramSocket实例,指定本地端口。
2. 通过DatagramSocket实例的receive方法接收
DatagramPacket.DatagramPacket中间就包含了通信的内容。
3. 通过DatagramSocket的send和receive方法来收和发DatagramPacket.
客户端(client):
1. 构造DatagramSocket实例。
2. 通过DatagramSocket实例的send和receive方法发送DatagramPacket报文。
3. 结束后,调用DatagramSocket的close方法关闭。
异常处理:
下列哪种异常是检查型异常,需要在编写程序时声明()
A.NullPointerException
B.ClassCastException
C.FileNotFoundException
D.IndexOutOfBoundsException
答案:C
分析:NullPointerException空指针异常
ClassCastException类型转换异常
IndexOutOfBoundsException索引超出边界的异常
以上这些异常都是程序在运行时发生的异常,所以不需要在编写程序时声明
Java出现OutOf MemoryError(OOM 错误)的原因有哪些?出现OOM错误后,怎么解决?
答:OutOf MemoryError这种错误可以细分为多种不同的错误,每种错误都有自身的原因和解决办法,如下所示:
java.lang.OutOfMemoryError: Java heap space
错误原因:此OOM是由于JVM中heap的最大值不满足需要。
解决方法:1) 调高heap的最大值,即-Xmx的值调大。2) 如果你的程序存在内存泄漏,一味的增加heap空间也只是推迟该错误出现的时间而已,所以要检查程序是否存在内存泄漏。
java.lang.OutOfMemoryError: GC overhead limit exceeded
错误原因:此OOM是由于JVM在GC时,对象过多,导致内存溢出,建议调整GC的策略,在一定比例下开始GC而不要使用默认的策略,或者将新代和老代设置合适的大小,需要进行微调存活率。
解决方法:改变GC策略,在老代80%时就是开始GC,并且将-XX:SurvivorRatio(-XX:SurvivorRatio=8)和-XX:NewRatio(-XX:NewRatio=4)设置的更合理。
java.lang.OutOfMemoryError: Java perm space
错误原因:此OOM是由于JVM中perm的最大值不满足需要。
解决方法:调高heap的最大值,即-XX:MaxPermSize的值调大。
另外,注意一点,Perm一般是在JVM启动时加载类进来,如果是JVM运行较长一段时间而不是刚启动后溢出的话,很有可能是由于运行时有类被动态加载进来,此时建议用CMS策略中的类卸载配置。如:-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled。
java.lang.OutOfMemoryError: unable to create new native thread
错误原因:当JVM向OS请求创建一个新线程时,而OS却由于内存不足无法创建新的native线程。
解决方法:如果JVM内存调的过大或者可利用率小于20%,可以建议将heap及perm的最大值下调,并将线程栈调小,即-Xss调小,如:-Xss128k。
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
错误原因:此类信息表明应用程序(或者被应用程序调用的APIs)试图分配一个大于堆大小的数组。例如,如果应用程序new一个数组对象,大小为512M,但是最大堆大小为256M,因此OutOfMemoryError会抛出,因为数组的大小超过虚拟机的限制。
解决方法:1) 首先检查heap的-Xmx是不是设置的过小。2) 如果heap的-Xmx已经足够大,那么请检查应用程序是不是存在bug,例如:应用程序可能在计算数组的大小时,存在算法错误,导致数组的size很大,从而导致巨大的数组被分配。
java.lang.OutOfMemoryError: request < size> bytes for < reason>. Out of swap space
错误原因:抛出这类错误,是由于从native堆中分配内存失败,并且堆内存可能接近耗尽。这类错误可能跟应用程序没有关系,例如下面两种原因也会导致错误的发生:1) 操作系统配置了较小的交换区。2)系统的另外一个进程正在消耗所有的内存。
解决办法:1) 检查os的swap是不是没有设置或者设置的过小。2) 检查是否有其他进程在消耗大量的内存,从而导致当前的JVM内存不够分配。
注意:虽然有时< reason>部分显示导致OOM的原因,但大多数情况下,< reason>显示的是提示分配失败的源模块的名称,所以有必要查看日志文件,如crash时的hs文件。
列举常见的运行时异常
答:ClassCastException(类转换异常)
比如 Object obj=new Object(); String s=(String)obj;
IndexOutOfBoundsException(下标越界异常)
NullPointerException(空指针异常)
ArrayStoreException(数据存储异常,操作数组时类型不一致)
BufferOverflowException(IO操作时出现的缓冲区上溢异常)
InputMismatchException(输入类型不匹配异常)
ArithmeticException(算术异常)
注意:运行时异常都是RuntimeException子类异常。
下面关于 Java.lang.Exception类的说法正确的是()
A.继承自 Throwable
B.不支持Serializable
C.继承自 AbstractSet
D.继承自FitelnputStream
答案:A
分析:Throwable是Exception和Error的父类,Exception虽然没有实现Serializable接口,但是其父类Throwable已经实现了该接口,因此Exception也支持Serializable。
Unsupported major.minor version 52是什么异常,怎么造成的,如何解决?
答:问题的根本原因是工程中某个jar包的版本(jar包编译时的所用的jdk版本)高于工程build path中jdk的版本,这个是不兼容的! 编程中遇到此异常Unsupported major.minor version 52.0(根据版本号,这里可以为其他数值,52是1.8jdk jar包与 1.8以下低版本jdk不匹配),在将build path中jdk的版本调整与jar包匹配后,解决异常。
try{}里有一个return语句,那么紧跟在这个try后的finally{}里的code会不会被执行,什么时候被执行,在return前还是后?
答:会执行,在方法返回调用者前执行。Java允许在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是记录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,这会对程序造成很大的困扰,C#中就从语法上规定不能做这样的事。
(也许你的答案是在return之前,但往更细地说,我的答案是在return中间执行,请看下面程序代码的运行结果:
public classTest {
/**
* @paramargs add by zxx ,Dec 9, 2008
*/
public static voidmain(String[] args) {
// TODO Auto-generated method stub
System.out.println(newTest().test());;
}
static int test()
{
int x = 1;
try
{
returnx;
}
finally
{
++x;
}
}
}
执行结果
运行结果是1,为什么呢?主函数调用子函数并得到结果的过程,好比主函数准备一个空罐子,当子函数要返回结果时,先把结果放在罐子里,然后再将程序逻辑返回到主函数。所谓返回,就是子函数说,我不运行了,你主函数继续运行吧,这没什么结果可言,结果是在说这话之前放进罐子里的。
Java 语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别如何使用?
答:Java 通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java 中,每个异常都是一个对象,它是Throwable 类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。Java 的异常处理是通过5 个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throw)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理;try用来指定一块预防所有“异常”的程序;catch 子句紧跟在try块后面,用来指定你想要捕捉的“异常”的类型;throw 语句用来明确地抛出一个“异常”;throws用来标明一个成员函数可能抛出的各种“异常”;finally 为确保一段代码不管发生什么“异常”都被执行一段代码;可以在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try 语句,“异常”的框架就放到栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种“异常”进行处理,栈就会展开,直到遇到有处理这种“异常”的try 语句。
运行时异常与受检异常有何异同?
答:异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异常。异常和继承一样,是面向对象程序设计中经常被滥用的东西,神作《Effective Java》中对异常的使用给出了以下指导原则:
不要将异常处理用于正常的控制流(设计良好的API不应该强迫它的调用者为了正常的控制流而使用异常)
对可以恢复的情况使用受检异常,对编程错误使用运行时异常
避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常发生)
优先使用标准的异常
每个方法抛出的异常都要有文档
保持异常的原子性
不要在catch中忽略掉捕获到的异常
(异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。)
类ExampleA 继承Exception,类ExampleB 继承ExampleA
有如下代码片断:
try{
throw new ExampleB("b")
}catch(ExampleA e){
System.out.println("ExampleA");
}catch(Exception e){
System.out.println("Exception");
}
}
请问执行此段代码的输出是什么?
答:输出:ExampleA。(根据里氏代换原则[能使用父类型的地方一定能使用子类型],抓取ExampleA类型异常的catch块能够抓住try块中抛出的ExampleB类型的异常)
补充: 比此题略复杂的一道面试题如下所示(此题的出处是《Java编程思想》),说出你的答案吧!
package com.bjsxt;
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}
class Human {
public static void main(String[] args)
throws Exception {
try {
try {
throw new Sneeze();
}
catch ( Annoyance a ) {
System.out.println("Caught Annoyance");
throw a;
}
}
catch ( Sneeze s ) {
System.out.println("Caught Sneeze");
return ;
}
finally {
System.out.println("Hello World!");
}
}
}
输出为:
Caught Annoyance
Caught Sneeze
Hello World!
Error和Exception的区别
Error类,表示仅靠程序本身无法恢复的严重错误,比如说内存溢出、动态链接异常、虚拟机错误。应用程序不应该抛出这种类型的对象。假如出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。所以在进行程序设计时,应该更关注Exception类。
Exception类,由Java应用程序抛出和处理的非严重错误,比如所需文件没有找到、零作除数,数组下标越界等。它的各种不同子类分别对应不同类型异常。可分为两类:Checked异常和Runtime异常
Java异常处理try-catch-finally的执行过程
try-catch-finally程序块的执行流程以及执行结果比较复杂。
基本执行过程如下:
1)程序首先执行可能发生异常的try语句块。
2)如果try语句没有出现异常则执行完后跳至finally语句块执行;
3)如果try语句出现异常,则中断执行并根据发生的异常类型跳至相应的catch语句块执行处理。
4)catch语句块可以有多个,分别捕获不同类型的异常。
5)catch语句块执行完后程序会继续执行finally语句块。
finally语句是可选的,如果有的话,则不管是否发生异常,finally语句都会被执行。需要注意的是即使try和catch块中存在return语句,finally语句也会执行,是在执行完finally语句后再通过return退出。
异常处理中throws和throw的区别
1)作用不同:
throw用于程序员自行产生并抛出异常;
throws用于声明在该方法内抛出了异常
2) 使用的位置不同:
throw位于方法体内部,可以作为单独语句使用;
throws必须跟在方法参数列表的后面,不能单独使用。
3)内容不同:
throw抛出一个异常对象,且只能是一个;
throws后面跟异常类,而且可以有多个。
最后
感谢阅读,点赞收藏+关注!更多Java进阶学习资料、2022大厂面试真题,完整资料已经给大家打包完毕,需要资料的小伙伴 可以点击此链接添加备注暗号进行免费领取
0