异常
(一)概念
1.异常的产生
异常就是程序中发生的所有不正常的现象,类似于人会生病是一样的道理,异常是Java的重大特色,合理地使用异常处理,可以让我们程序更加健壮.
异常是导致程序中断执行的一种指令流,异常一旦出现并且没有进行合理的处理的话,那么程序就将中断执行.
一旦产生异常之后发现产生异常的语句以及后面的语句将不再执行,默认情况下是进行异常信息的输出,而后自动结束程序的执行.
程序员要做的事情是,即使出现了异常.那么也应该让程序正确的执行完毕.好的程序是在它出现错之后我还是希望它能够正常的执行完毕.
(二)处理异常
1.tryCatch异常处理的格式
如果要想进行异常的处理,在Java之中提供了三个关键字:try ,catch ,finally,而这三个关键字的使用语法如下:
try{
//有可能出现异常的语句
}[ catch (异常类型 对象)] {
//异常处理;
}[ catch (异常类型 对象)] {
//异常处理;
}[ catch (异常类型 对象)] {
//异常处理;
}[finally{
//不管是否出现异常,都执行的统一代码
}]
1、try代码块:监视代码块的执行,发现对应的的异常则跳转至catch,若无catch则直接到finally块。
2、catch代码块:用于处理try捕获到的异常,要么处理,要么向上抛出。
3、finally代码块:不管是否有异常,都必执行,一般用来清理资源,释放连接等。然而有以下几种情况不会执行到这里的代码:
程序所在的线程死亡。
关闭CPU。
代码执行流程未进入try代码块。
代码在try代码块中发生死循环、死锁等状态。
在try代码块中执行了System.exit()操作。
对于以后的操作组合:
1 try…catch
2 try…catch…finally
3 try…final (不建议出现,语法不适合开发)
出现异常的目的是为了解决异常,为了能够进行异常的处理,可以使用异常类中提供的printStackTrace()方法进行异常信息的完整输出
/**
-
- 如果没输入参数报异常.
-
- 用户输入的参数不是数字.
-
- 被除数为零
- @param args
*/
public static void main(String[] args) {
System.out.println(“1,除法计算开始”);
try {
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
System.out.println(“2.除法计算”);
int a = (x / y); // 这里出现了异常了(它后面的语句都不执行了 try代码块里面的)
System.out.println(“看看这个语句是否还打印.”); // 不执行
} catch (ArithmeticException e) {//捕获运算异常
// System.out.println(“出现异常了”); //描述信息不完整
e.printStackTrace(); //打印了异常的信息很完整(出了什么异常,告诉你在第几行代码出现的异常)
} catch (ArrayIndexOutOfBoundsException e) {//捕第二个异常
e.printStackTrace(); //打印异常信息
} finally {
System.out.println("!!!不管是否出现异常,我都要完整的执行");//即使出现异常 也会出现这行代码
}
System.out.println(“3.除法计算结束了”);//异常都处理完了,这个也会出现了
}
异常捕获了程序很健壮,但是以上的异常都已经知道了,你还让它出现,这个绝对就是技术问题了.正常情况下应该用if else来及时判断来给与提示
2.try异常返回值问题
如果try语句里有return,返回的是try语句块中变量值。 详细执行过程如下:
如果有返回值,就把返回值保存到局部变量中;
执行jsr指令跳到finally语句里执行;
执行完finally语句后,返回之前保存在局部变量表里的值。
如果try,finally语句里均有return,忽略try的return,而使用finally的return.
3.异常的处理流程
异常类的继承结构
经过异常类的观察可以发现所有的异常类都是Throwable的子类,而在Throwable下有两个子类
1.程序在执行过程中发生了异常之后,会由JVM自动根据异常的类型产生实例化一个与之类型匹配的异常类对象
比如是算术出错,就是算术异常, 如果是数组越界就是数组越界异常
2.产生异常之后会先判断当前的语句上是不是有异常处理(try捕获),如果没有异常处理,那么就交给JVM进行默认的异常处理
jvm默认的异常处理是,输出以后信息,然后结束程序的调用(后面的代码都不执行了)
3.如果此时存在有异常的捕获(try捕获),那么会由try语句来捕获产生的异常类实例化对象,它抓住这个对象,然后与try语句后的每一个catch进行比较,如果现在有符合捕获类型,则使用当前的catch的语句来进行异常的处理,如果不匹配,则向下继续匹配其它的catch
4.不管最后的异常处理是否能够匹配,都要向后执行,如果此时程序中存在有finally语句,那么就先执行finally中的代码,但是执行完毕后需要根据之前的catch匹配结果来决定如何执行,如果之前已经成功的捕获了异常,那么就继续执行finally之后的代码,如果之前没有成功的捕获异常,那么就将此异常交给jvm进行默认处理(输出异常信息,而后结束程序的执行)
异常处理整个过程就好比方法重载一样,根据catch后面的参数类型进行匹配,但是所有的Java对象都存在有自动的向上转型的操作支持,也就是说如果要真的匹配类型,简单的做法就是匹配Excepton 就够了,但是在编写捕获多个异常的时候,捕获的范围大的异常一定要放在捕获范围小的异常的后面.否则程序编译错误(语法错误)
虽然使用exception捕获了很方便,但是这样也很不好,因为所有的异常都会按照同样的一种方式进行处理,如果在一些要求严格的项目中,异常一定要分开处理更好.
4.throws关键字和throw关键字
throws关键字
throws关键字主要用于方法声明上,指的是当方法之中出现异常后交由被调用处来处理,调用了使用throws声明的方法之后,不管操作是否出现了异常,都必须使用try…catch来进行我们的异常的处理,如果在main方法继续抛出异常,那么这个异常就将交给jvm进行,也就是采用了我们默认的处理方式,输出异常信息,而后结束程序的调用.
主方法不要加上throws,因为程序如果出错了,也希望正常的结束程序的调用,如果交给jvm调用就不会正常的结束方法的调用.
public class lmxi {
//声明异常
public static int div(int x, int y) throws Exception {
return x / y;
}
/**
* 告诉调用者去处理
*/
@Test
public void ceui1() {
try {
lmxi.div(1, 0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
throw与throws的区别:
Throw:
强调抛出异常的动作,用于方法体之中,后面跟的是异常对象;
Throws:
强调的是可能会出现的某种问题,让调用者知道,然后给出一个解决方案;用于方法的声明上,后面跟的是异常类类名;
throw关键字
在程序之中可以直接使用throw手工的抛出一个异常类的实例化对象.
throw与throws的区别[面试题]
Throw:
在方法之中人为的抛出一个异常类对象(这个异常类对象可能是自己实例化或者抛出已存在的)
Throws:
在方法的声明上使用,表示的是此方法在调用时必须处理异常,如果出现就处理,如果不出现就正常执行
5.请解释error和exception的区别[面试题]
1.Error : 指的是JVM错误,即:此时的程序还没有执行,如果没有执行用户无法处理,比如,错误,找不到或者无法加载主类(编译的时候无法找到这个类)
2.Exception: 指的是程序运行中产生的异常,用户可以处理
也就是所谓的异常处理指的就是所有Exception以及它的子类异常,我们才能进行处理
6.异常的使用格式[重要的代码模型]
需求:
要求定义一个div()方法,要求在方法进行计算之前打印提示信息,在计算结束完毕也打印提示信息,如果在计算之中产生了异常,则交给被调用处进行处理.
(三)其它
1.Throwable类介绍
Throwable是所有异常的父类Throwable类下面有两个常见子类:
1:Error
错误,非常严重的问题,通常这类问题,大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题
例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述
2:Exception
这个类型的问题,称为编译时期的异常,在代码编写的时候,问题就会暴露出来,程序员必须给出相应的解决方案,方可代码编译通过;
Exception则细分为两类,受检异常(check)需要我们手动try/catch或者在方法定义中throws,编译器在编译的时候会检查其合法性。非受检异常(uncheck)则不需要我们提前处理。
2.Throwable类常用方法
public string getMessage():返回异常发生时的详细信息
public string toString():返回异常发生时的简要描述
public string getLocalizedMessage():返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以声称本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同
public void printStackTrace():在控制台上打印Throwable对象封装的异常信息
3.什么是编译时期和运行时期异常区别
Java中的异常被分为两大类:编译时异常和运行时异常。
所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常
编译时异常
Java程序必须显示处理,否则程序就会发生错误,无法通过编译
运行时异常
无需显示处理,也可以和编译时异常一样处理
4.异常处理最佳实践
好了,前面简单介绍了异常的分类以及try/catch/finally的注意事项,现在可以总结一下我们在异常处理的时候有哪些”最佳实践“了。
当需要向上抛出异常的时候,需根据当前业务场景定义具有业务含义的异常,优先使用行业内定义的异常或者团队内部定义好的。例如在使用dubbo进行远程服务调用超时的时候会抛出DubboTimeoutException,而不是直接把RuntimeException抛出。
请勿在finally代码块中使用return语句,避免返回值的判断变得复杂。
捕获异常具体的子类,而不是Exception,更不是throwable。这样会捕获所有的错误,包括JVM抛出的无法处理的严重错误。
切记更别忽视任何一个异常(catch住了不做任何处理),即使现在能确保不影响逻辑的正常运行,但是对于将来谁都无法保证代码会如何改动,别给自己挖坑。
不要使用异常当作控制流程来使用,这是一个很奇葩也很影响性能的做法。
清理资源,释放连接等操作一定要放在finally代码块中,防止内存泄漏,如果finally块处理的逻辑比较多且模块化,我们可以封装成工具方法调用,代码会比较简洁。
5.慎用异常
异常对性能不利。抛出异常首先要创建一个新的对象,Throwable接口的构造函数调用名为fillInStackTrace()的本地同步方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,Java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。
(四)异常的使用
1.自定义异常
自定义异常概述:
当我们需要一个异常类描述一个java中没有描述的问题时,那么就需要我们自定义一个描述问题的类,这个类就是自定义异常类;
自定义异常是继承exception就可以了
自定义异常类必须继承java中已经存在的那些异常类中的其中一个;
继承的时候,可以继承Exception也可以继承RuntimeException;
如果继承Exception:
那么此时的自定义异常类就是一个编译时期的异常类;如果在方法中出现这个类型的问题,那么就必须给出相应的解决方案;
如果继承RuntimeException
那么此时的自定义异常类就是一个运行时期的异常类;如果在方法中出现这个类型的问题,那么就可以处理也可以不处理;
自定义异常步骤
1.创建自定义异常类
2.在方法中通过throw关键字抛出异常对象
3.如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理,否则在方法的声明处通过throws关键字指明要抛给方法调用者的异常,继续下一步的操作
4.在出现异常的方法的调用者中捕获并处理异常
public class MyException extends Exception {//创建自定义异常,继承Exception类
public MyException(String ErrorMessagr) { // 构造方法
super(ErrorMessagr); // 父类构造方法
}
}
字符串ErrorMessage是要输出的错误信息,若想抛出用户自定义的异常对象,要使用throw关键字
在项目中创建类Tran,该类中创建一个带有int型参数的方法avg(),该方法用来检查参数是否小于0或大于100,如果参数小于0或大于100,则通过throw关键字抛出一个MyException异常对象,并在main()方法中捕捉该对象.
public class Tran { // 创建类
// 定义方法,抛出异常
static int avg(int number1, int number2) throws MyException {
if (number1 < 0 || number2 < 0) { // 判断方法中参数是否满足指定条件
throw new MyException(“不可以使用负数”); // 错误信息
}
if (number1 > 100 || number2 > 100) { // 判断方法中参数是否满足指定条件
throw new MyException(“数值太大了”); // 错误信息
}
return (number1 + number2) / 2; // 将参数的平均值返回
}
public static void main(String[] args) { // 主方法
try { // try代码块处理可能出现异常的代码
int result = avg(102, 150); // 调用avg()方法
System.out.println(result); // 将avg()方法的返回值输出
} catch (MyException e) {
System.out.println(e); // 输出异常信息
}
}
}
2.异常demo代码
3.异常工具类与使用
效果
postman显示内容
4.不在控制台打印堆栈信息
自定义异常打印堆栈信息很烦,没有太多的用处
解决办法,在 自定义异常类 中重写空的fillInStackTrace方法
/**
- 重写这个方法目的是不让在控制台打印堆栈信息
*/
@Override
public Throwable fillInStackTrace() {
return this;
}
效果:
5.如果自定义异常抛不出去(被源码里面的上层捕获了)
反射
(一)概念
1.使用类步骤
正常情况下使用类步骤
使用 import导入类所在的包,(类:java.long.Class)
明确使用类名称或接口名称定义对象
通过关键字new进行类对象实例化(构造方法:java.long.reflect.Constructor)
产生对象可以使用”对象.属性”进行类中属性的调用(属性: java.long.reflect.Field)
通过’对象.方法()’调用类中的方法(方法: java.long.reflect.Method)
反射过程使用类步骤
不需要有明确类型的对象,所有的对象使用Object表示.
可以直接利用Object与反射机制的混合调用类中的方法.
2.Class类
Class类是整个反射的操作源头,而这个类的定义如下:
Class是final修饰的, 同时有泛型, 需要注意,反射的泛型几乎没用,使用的时候就用?号就行(泛型根本出不来,使用时候没有任何意义.)
Class类的构造方法是private,由JVM创建。
如果要想使用Class类进行操作,必须首先产生Class类的实例化对象:
获取class文件对象的三种方式
1.可以通过Object类中的方法获取(不会使用)
Class<?> getClass() 返回此 Object 的运行时类。
2.可以通过类名.class属性获取(普通程序员使用者用这种方式最多)
应用场景:确定类型等
java会为任意的数据类型,包含基本数据类型和引用数据类型设置一个class属性
3.可以通过Class类中的静态方法获取(程序设计人员用这种方式最多)
应用场景:通过配置获得字符串等
static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。
注意:一个类的class文件对象只有一个
3.基本概念
4.反射获取信息API合集
5.内省
内省是不知道对象是什么的情况下来去给对象复制,创建对象的机制,写框架代码的时候会用到,平时时候用不到.
内省(Introspector)是Java 语言对 Bean 类属性、事件的一种缺省处理方法。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。
通过 getName/setName 来访问 name 属性,这就是默认的规则。
Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API 可以使你不需要了解这个规则,这些 API 存放于包 java.beans 中,一般的做法是通过类 Introspector 的 getBeanInfo方法 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。
1、Introspector类:
Introspector 类为通过工具学习有关受目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。
static BeanInfo getBeanInfo(Class<?> beanClass)
在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。
2、BeanInfo类:
该类实现此 BeanInfo 接口并提供有关其 bean 的方法、属性、事件等显式信息。
MethodDescriptor[] getMethodDescriptors()
获得 beans MethodDescriptor。
PropertyDescriptor[] getPropertyDescriptors()
获得 beans PropertyDescriptor。
Properties 属性文件工具类的使用
3、PropertyDescriptor 类:
PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。
Method getReadMethod()
获得应该用于读取属性值的方法。
Method getWriteMethod()
获得应该用于写入属性值的方法。
4、MethodDescriptor 类:
MethodDescriptor 描述了一种特殊方法,
即 Java Bean 支持从其他组件对其进行外部访问。
Method getMethod()
获得此 MethodDescriptor 封装的方法。
6.动态加载类
编译时加载类是静态加载类,
new 创建对象是静态加载类,在编译时刻就需要加载所有可用使用到的类,如果有一个用不了,那么整个文件都无法通过编译
运行时加载类是动态加载类
Class c = Class.forName(“类的全名”),不仅表示了类的类型,还表示了动态加载类,编译不会报错,在运行时才会加载,使用接口标准能更方便动态加载类的实现。功能性的类尽量使用动态加载,而不用静态加载。
很多软件比如QQ,360的在线升级,并不需要重新编译文件,只是动态的加载新的东西
(二)使用
1.访问构造方法
2.访问成员变量
3.访问方法
注解
(一)概念
1.理解java注解
注解就相当于对源代码打的标签,给代码打上标签和删除标签对源代码没有任何影响。有的人要说了,你尽几把瞎扯,没有影响,打这些标签干毛线呢?其实不是这些标签自己起了什么作用,而且外部工具通过访问这些标签,然后根据不同的标签做出了相应的处理。这是注解的精髓,理解了这一点一切就变得不再那么神秘。
例如我们写代码用的IDE(例如 IntelliJ Idea),它检查发现某一个方法上面有@Deprecated这个注解,它就会在所有调用这个方法的地方将这个方法标记为删除。
访问和处理Annotation的工具统称为APT(Annotation Processing Tool)
注解需要配合反射来使用,用反射来读取注解的内容.进行其它的操作
2.注解的作用以及起源
在出现注解之前,xml被广泛的应用在描述元数据,不知道从什么时候开始,一些开发人员和架构师发现xml的维护越来越糟糕,他们希望使用一些和代码紧耦合的东西,而不是像xml那样和代码是松耦合的代码描述.
假如你想为应用设置很多的常量或参数,这种情况下,XML是一个很好的选择,因为它不会同特定的代码相连。如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来,开发人员也必须认识到这点。
3.元注解
用来给其他注解打标签的注解,即用来注解 其他注解 的注解。元注解共有6个。从上面的基本注解的源代码中就会看到使用了元注解来注解自己。
https://www.cnblogs.com/lyy-2016/p/6288535.html
4.注解的生命周期
@Retention– 定义该注解的生命周期。
RetentionPolicy.SOURCE – 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
RetentionPolicy.CLASS – 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。
RetentionPolicy.RUNTIME– 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
https://blog.youkuaiyun.com/asdgbc/article/details/70196749
5.注解的作用域
表示该注解可以用于一个类中的那些属性及方法上,如果作用域类型有多个用英文逗号分隔
下面是注解的作用域的列表:
@Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
https://blog.youkuaiyun.com/u012992462/article/details/80675932
(二)使用
1.处理注解
Annotation必须使用工具来处理,工具可以提取Annotation里包含的元数据.根据这些元数据增加额外的功能.
网络编程(Socket)
(一)概念
1.网络程序设计
网络程序设计是指编写与其他计算机进行通信的程序,Java已经将网络程序所需要的东西封装成了不同的类,只要创建这些类的对象,使用相应的方法,即使设计人员不具备有关的网络知识,也可以编写出高质量的网络通信程序.
两台计算机要想通过网络进行通信,它们需要满足一些必然的条件
1.两台主机需要唯一的标识,用来表示它们所处的身份,所在的位置(就是ip地址)
2.需要共同的语言,否则就会出现言语不通,无法交流(这就是协议)
3.每台主机都需要有相应的端口号,一台主机上可以运行多台不同的应用程序,如果辨别不同的运行程序的通信,我们需要使用端口号来进行区分
TCP/IP是目前世界上应用最为广泛的协议.,它是以TCP和IP为基础的不同的层次上的多个协议的集合.也称为 TCP/IP协议族或者TCP/IP协议栈.
两个主机之间要进行通讯,都要遵守 TCP 和IP的协议.
TCP全称为 传输控制协议 (Transmission Control Protocol)
IP全称为 互联网协议 (Internet Protocol)
TCP/IP模型
5层
1.物理层:
网线,双脚线,网卡等等
2.数据链路
3.网络
4.传输
5.应用
HTTP超文本传输协议
TCP/IP协议
FTP 文件传输协议
SMTP 简单邮件传送协议
Telnet 远程登录服务
IP地址
每台计算机的唯一标识,类似于每个人的手机号码, 张三想和李四进行通信,就得知道手机号码,通过手机号定位到李四的人, ip地址也是这样.如果想换另外一个计算机通信,就得知道那个人的ip地址.
ip地址标准:
ipv4
长度为32位的二进制 比如192.168.0.1
ipv6
端口
区分不同的应用程序,一个主机可以同时运行多个程序,比如qq和迅雷,旺旺等等,如何保证一台主机给另外一台主机发送的信息被对方程序正确的接收. 每一个应用都有唯一的端口号来标识不同的应用程序,
端口号的范围为065535,其中01023为系统所保留下来分配给系统用的服务.我们自定义端口号建议用1023后面的端口号
ip地址和端口号组成了所谓的Socket,Socket是网络上运行的程序直接双向通信链路的终结点,是TCP和UDP的基础.
什么是网络通信协议
网络通信协议 类似于 交通系统中的交通法规,网络通信协议规定网上传输的数据格式 传输速度,数据大小等规定.
常用网络通信协议:
TCP/IP协议
UDP协议
其他协议:FTP,HTTP,HTTPS
(二)Java中的网络支持
Java提供的网络功能的四大类:
1.InetAddress : 用于标识网络上的硬件资源(比如标识Ip地址相关信息)
2.URL : 统一资源定位符,通过URL可以直接读取或写入网络上的数据.
3.Socket: 使用TCP协议实现网络通信的Socket相关的类.
4.Datagram: 使用UDP协议,使用Datagram进行通信时,数据保存在数据报中,通过在网络中发送数据报进而实现网络的通信.
1.InetAddress 类
InetAddress类用于标识网络上的硬件资源,表示互联网协议(IP)地址.
java.net包中的InetAddress类是与ip地址相关的类,利用该类可以获取ip地址主机地址等信息
/**
-
静态方法获取本机实例
-
@throws UnknownHostException
*/
@Test
public void ceui1() throws UnknownHostException {InetAddress localHost = InetAddress.getLocalHost();//获取本机的实例(直接syso输出的话获取的是 计算机名和ip地址的信息)
//输出:AS5I1LSDKRB5TOU/192.168.192.1String hostName = localHost.getHostName(); //获取主机名
//输出:AS5I1LSDKRB5TOUString hostAddress = localHost.getHostAddress();//获取主机地址
//输出:192.168.192.1byte[] address = localHost.getAddress();//获取字节数组形式的ip地址
String toString = Arrays.toString(address);
//输出:[-64, -88, -64, 1]
}
/**
- 根据机器名获取InetAddress实例
- @throws UnknownHostException
*/
@Test
public void ceui() throws UnknownHostException {
/根据机器名获取InetAddress实例/
InetAddress address = InetAddress.getByName(“AS5I1LSDKRB5TOU”);//AS5I1LSDKRB5TOU/192.168.192.1
String hostName = address.getHostName(); //计算机名 输出:AS5I1LSDKRB5TOU
String hostAddress = address.getHostAddress(); //ip地址 输出:192.168.192.1
}
/**
-
根据ip地址获取实例
*/
@Test
public void ceui2() throws UnknownHostException {InetAddress byName = InetAddress.getByName(“192.168.192.1”);//输出:AS5I1LSDKRB5TOU/192.168.192.1
String hostName = byName.getHostName(); //计算机名 输出 AS5I1LSDKRB5TOU
String hostAddress = byName.getHostAddress();//ip地址 输出 192.168.192.1
}
2.URL类
URL
1.Uniform Resource Locator 统一资源定位符,表示Internet上某一资源的地址
2.URL由两部分组成:协议名称和资源名称,中间用冒号隔开.
网站: http://www.imobile.com.cn
http: 表示 http协议
www.imobile.com.cn : 表示资源名称
/**
-
URl 类的基本使用
-
@throws MalformedURLException
*/
@Test
public void ceui1() throws MalformedURLException {//创建 URL实例
URL imooc = new URL(“https://www.imooc.com”);/* 根据已有的url实例创建新的url
?后面表示参数String protocol = url.getProtocol();//获取协议信息.输出https
String host = url.getHost();//资源所在的主机 输出www.imooc.com/*为什么端口号是 -1 , 因为我们在指定url资源的时候并没有指定端口号,
- 没有指定端口号就使用的是协议默认的端口号,http协议使用的默认是80端口号
*为什么端口号返回的是 -1 呢? - 因为我们在创建url实例的时候并没有指定端口号,它用的确实是默认端口号,也就是80
- 然后对于url.getPort() 方法而言,如果未指定端口号,它的返回值就是-1
- */
int port = url.getPort();//url的端口号 输出-1
String path = url.getPath();//获取文件路径 输出 /index.html
String file = url.getFile();//文件名 输出/index.html?username=tom
String ref = url.getRef();//相对路径 输出test
String query = url.getQuery();//查询字符串 输出username=tom
- 没有指定端口号就使用的是协议默认的端口号,http协议使用的默认是80端口号
}
使用URL读取网页内容
1.通过URl对象的openStream()方法可以得到指定资源的输入流
2.通过输入流可以读取,访问网络上的数据.
/**
* 使用url读取网页内容
*/
@Test
public void ceui2() throws IOException {
URL url = new URL(“http://www.baidu.com”);
InputStream is = url.openStream(); //获取url对象所表示的资源的字节输入流
InputStreamReader isr = new InputStreamReader(is, “utf-8”);//将字节输入流转换为字符输入流
//给字符流添加缓冲,提高读取效率
BufferedReader br = new BufferedReader(isr);
String data = br.readLine();//一次读取一行
while (data != null) {
System.out.println(data);
data = br.readLine(); //读取下一行
}
br.close();
isr.close();
is.close();
/*控制台信息就是html页面 :
* <!DOCTYPE html>
}
3.基于TCP的Socket用法
TCP协议是面向连接,可靠的,有顺序的,以字节流的方式发送数据
基于TCP协议实现网络通信的类:
1.客户端的Socket类
2.服务器端的ServerSocket类
Socket通信模型
两台主机需要通信,就必然的存在着一个服务端,另外一个是客户端.
1.服务端建立一个ServerSocket绑定相应的端口,并且在指定的端口进行监听,等待客户端的连接
2.客户端创建连接的socket并且向服务器端发送请求
3.服务器收到请求,并且接受到客户端的请求信息,一旦接到客户端的请求以后会创建一个连接的socket用来和客户端的socket进行通信.
4.服务端和客户端通过相关的输入流和输出流开始通信,进行数据的交换,数据就发送和接收以及数据的响应等等.
5.在客户端和服务器端双方通信完以后,我们需要分别关闭客户端和服务端的socket进行通信的端口.
Socket实现步骤
1.创建ServerSocket和Socket建立,这是实现网络通信的基础
2.打开连接到Socket的输入/输出流进行相关的通信
3.根据相应的协议,对Socket进行读/写操作
4.在通信完成以后关闭输入输出流,关闭Socket
一个服务端和一个客户端通信
一个服务端和多个客户端通信(使用多线程)
4.UDP编程
UDP协议(用户数据报协议)是无连接,不可靠的,传输的顺序也是不固定的,它的速度相对来说会比较快
UDP协议以数据报作为数据传输的载体.当我们使用UPD进行数据传输时,会将数据封装成Datagram(数据报)当中去标识指明数据所达的目的地以及相应的端口号,然后将数据报传输出去.
DatagramoPacket
表示数据报包,用来表示UDP通信中的数据单元
DatagramSocket
进行端到端通信的类,实现基于UDP的Socket通信
服务器和客户端一对一通信
5.网络编程总结
1.关于多线程优先问题
服务器需要不停的与多个客户端进行通信,服务器端程序需要不停的监听客户端的请求,就需要使用多线程了,既然是死循环,线程就需要优先级问题.如果没有设置优先级可能会导致在运行时效率低下,速度比较慢.
2.是否关闭输出流和输入流
对于同一个socket,如果关闭了输出流,则与该输出流关联的socket也会被关闭,所以一般不用关闭流,直接关闭socket即可
3.客户端向服务器端发送的数据都是通过字符串的形式进行交互的,而实际的运用当中,客户端向服务端发送的信息更多的都是以对象的形式进行传输,我们把用户名密码等等信息可以封装成user对象,传递的是一个user对象,这就是面向对象的思想,这时候就可以去使用ObjectOutputStream ObjectInputStream来实现对象的传输.
4.socket传递文件
可以使用 FileOutputStream 去读取文件中的数据,
6.//**************************
7.UDP与TCP协议
TCP协议的特点
TCP:传输控制协议
特点:面向有连接,安全,数据是完整,但是效率低
实际应用:迅雷
UDP协议的特点
UDP:用户数据报协议
特点:面向无连接,不安全,数据是不完整,但是效率高
实际应用:共屏,QQ视频
(三)网络编程源码
基础加强XML
(一)概述
XML概述
XML:Extensible(可扩展的) Markup(标记的) Language(语言)
这是一门独立的语言,与Java没有关系,但是Java,C,C#都可以使用它
可扩展:扩展性特别强,基本上任何语言都可以使用它
标记: <开始标记> 内容 </结束标记>
XML语言发展史:
XML一共有两个版本 XML1.0 XML1.1(没人用)
我们开发中1000%绝对用的XML1.0
XML语言的作用:
a.保存数据(传输数据)
<persons>
<person id="01010001">
<name>张三</name>
</person>
<person id="0202002">
<name>李四</name>
</person>
</persons>
b.给框架做配置文件(数据库)
(二)XML语法
1.XML的文档声明
常见一个文件,后缀必须是.xml
文档声明的格式:
<?xml 属性名="属性值" 属性名="属性值" ?>
属性有两个:
version 值必须是1.0 或者 1.1 (我们绝对使用1.0)
encoding 文件的编码 默认是UTF-8(建议写上)
注意:使用Eclipse可以自动生成带有文档声明的XML文档
2.XML的注释
<!-- 注释
可是当做单行注释
也可以当做多行注释
-->
快捷键: ctrl+shift+/ 自动出现注释
3.XML的标签/标记/元素(Element)
格式:
<开始>内容</结束>
注意事项:
1.内容 可以是普通文本,也可以是其他标签
2.标签名 不能使用特殊符号,不建议使用xml,XMl,xML开头或者结尾
3.如果是空元素,建议写成自闭合标签, <a></a>---------> <a/>
重要:
一个XML 只有一个根标签(根标签:最最最外层那个标签)
4.XML元素的属性(attribute)
每一个元素都可以添加属性,但是属性必须添加到开始元素中
格式:
<开始元素 属性名="属性值" 属性名="属性值" ></结束元素>
注意:
a. 属性值 必须用 ""或者'' 引起来,(建议用双引号)
b. 一个元素中,可以有0-N个属性,但是不能有相同名字的属性
c. 属性名必须以字母开头,且中间不能用特殊符号
5.XML的转译字符
需求: 要求写一个标签,标签内容:
if(5>10 || 6<8){
System.out.println("傻了");
}
如果XML中出现以下特殊符号,那么必须转译
> >
< <
" "
' '
& &
建议使用浏览器查看XML的最终效果
6.XML的CDATA区
需求:要求写一个标签,标签内容:
String s = "abc";
int count = 0;
for(int i = 0;i < s.length() ; i++){
char c = s.charAt(i);
if(c >= 'a' && c <= 'z'){
count++;
}
}
System.out.println("count = " + count);
当标签内容出现大量需要转译的字符时,
建议把这些大量需要转译的字符,直接写到CDATA区中
CDATA区的格式:
<![CDATA[
大量需要转译的字符
]]>
(三)XML约束
什么叫XML约束:
规定XML中每个标签,以及每个标签的属性,标签的内容
一.DTD约束 ***********
什么是DTD约束: Document Type Definition(文档类型定义约束)
重点:我们开中的重点,不是写DTD约束,而是根据DTD约束写出符合要求的XML
根据DTD写XML的步骤
1.在你的XML中 引入DTD约束文档
在DTD文件中拷贝一行代码:
<!DOCTYPE beans SYSTEM "bean.dtd">
根标签名字 本地 DTD文件的名字
2.根据Eclipse的提示编写XML即可
根据内容模板 Content Model 来写子标签即可
* 表示该标签出现的次数>=0
? 表示该标签出现的次数<=1
+ 表示该标签出现的次数>=1
| 表示多个标签中选择一个
, 表示多个标签出现的先后顺序
在开发中借助Eclipse的智能提示,快速完成XML的编写
[了解]在XML中引入DTD约束的三种方式:
a.引用内部DTD,直接在XML中写DTD文档,这种写法只对该XML有效
b.引用外部本地DTD: 该DTD在本地或者公司内部网络
<!DOCTYPE beans SYSTEM "bean.dtd">
c.引用外部公共DTD: 该DTD在公共的网络上,任何人都可以使用
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
[了解]DTD的语法:
<!Element 定义元素
<!ATTLIST 定义属性
具体语法可以参照DTD文件
二.schema约束 ***********
什么是schema约束:
另外一种XML约束
\ 它比DTD约束更加强大:
a.数据类型更丰富
b.支持命名空间(类似Java包)
[重点重点重点]我们开发中的重点,不是编写schema文档,而是根据schema约束文档编写符合要求的XML
根据Schema文档编写XML的步骤:
1.在XML中引入schema约束文档
schema文档,本质也是XML,但是后缀不是xml,而是xsd
我们的XML 根据schema约束文档编写,schema约束文档根据官方文档
拷贝schema中的几行代码:
<beans xmlns="http://www.itcast.cn/bean"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.itcast.cn/bean bean-schema.xsd"
>
与DTD不一样,schema中拷贝过来的其实就是根标签的开始标签,我们要补充结束标签
2.根据eclipse的提示,完成XML的编写即可
根据内容模板 Content Model 来写子标签即可
* 表示该标签出现的次数>=0
? 表示该标签出现的次数<=1
+ 表示该标签出现的次数>=1
| 表示多个标签中选择一个
, 表示多个标签出现的先后顺序
[了解]schema支持命名空间
什么是命名空间:类似于Java中包
[了解]schema的语法
(四)XML解析
什么叫XML解析:
就是通过Java代码,将保持在XML中的数据提取出来
通过我们学过IO流,能解析,但是过程是十分繁琐
前辈们根据不同的解析方式,分别编写一些类和接口,称为解析器
解析方式:是指解析思想,解析理论
解析器: 是对解析方式的代码实现(API非常复杂)
解析开发包: 封装解析器繁琐的代码,提供简化的代码实现
常用的XML解析理论(解析方式)
a.Dom解析理论:
要求一次性把整个XML加载到内存,最终生成一个Document对象
缺点:
内存占用大,可能导致内存溢出
优点:
整个XML的结构是完整的,就可以对象XML中元素进行增删改查
b.SAX解析理论:
要求读取一行,解析一行,释放一行
缺点:
XML结构不完整
优点:
内存占用小,速度快
c.PULL解析理解:
Android系统内置默认的解析方式
在解析方式这种理论的基础上,开发了解析器(API非常繁琐)
**************************************************
常见的解析开发包:
JAXP:它是sun公司提供的解析开发包,即支持Dom解析也支持SAX解析
JDOM: dom4j兄弟
Jsoup: 我们一般用它来解析HTML
Dom4j: 是我们开发过程中最最最最常用的XML解析开发包
使用Dom4j第三方框架,解析XML文档前提
a.Dom解析理论的原理
将整个XML 加载到内存,开始解析最终生成一个Document对象
b.倒立Dom树模型
此Document对象就是一个倒立的Dom树模型
使用Dom4J第三方框架解析XML文档的步骤
1.添加classpath
a.在项目根目录下创建一个文件夹叫lib
b.拷贝第三方框架的字节码文件jar包
c.右键点击需要添加jar包---->build path --->add to build path
就是可以是JDK类一样使用框架中的类
2.解析步骤
使用Dom4j中的核心类,专门用来加载整个XML,并解析最后返回一个Document对象
核心类: SAXReader
总结:
1.使用DTD约束编写XML
2.使用schema约束编写XML
3.使用Dom4j解析开发包解析XML
步骤:
a.使用SAXReader 加载XML,生成Document对象
Document document = new SAXReader().read(new File("xxx.xml"));
b.获取根标签
Element rootElement = document.getRootElement();
c.获取属性(如果有)
String 属性值 = rootElement.attributeValue("属性名");
d.获取子标签
List<Element> elements = rootElement.elements();//获取所有的直接子标签
List<Element> elements = rootElement.elements("标签名");//获取所有的指定名字的子标签
e.遍历所有的子标签
循环反复cde步骤,直到最后一层标签为止
如果最后一层不是标签,而是文本???
调用标签的getText()或者getTextTrim();
BeanUtils框架
1.简介
综合案例(基础版)(框架底层,了解即可)
在开中我们一般用一种快速反射的方式创建对象******
Class clazz = 类名.class;
//使用快速方式的前提:
//a.必须有无参构造 b.必须是public修饰
Object obj = clazz.newInstance();
上面这个行代码,底层是下方法
Constructor con = clazz.getConstructor();
User user = (User) con.newInstance();
框架BeanUtils的使用******
//给某个对象的某个属性设置值
public static void setProperty(Object obj,String name,Object value);
对象 属性名 具体的值
//获取某个对象的某个属性值
public static String getProperty(Object obj,String name);
对象 属性名
//用的最多的方法:一次性给对象的所有属性赋值
public static void populate(Object bean,Map map);
2.map和bean互相转换
Base64
(一)使用
工具类打开就能使用
UUID
1.生成不带横岗的uuid字符串(可以当做主键)
UUID.randomUUID().toString().replaceAll("-", “”)