String编码UTF-8和GBK的区别

  • GBK编码:是指中国的中文字符,其实它包含了简体中文与繁体中文字符,另外还有一种字符“gb2312”,这种字符仅能存储简体中文字符
  • UTF-8编码:它是一种全国家通过的一种编码,如果你的网站涉及到多个国家的语言,那么建议你选择UTF-8编码

GBK和UTF-8有什么区别

UTF-8编码格式很强大,支持所有国家的语言,正因为它的强大,才会导致它占用的空间大小要比GBK大,对于网站打开速度而言,也是有一定影响的。

GBK编码格式,它的功能少,仅限于中文字符,当然它所占用的空间大小会随着它的功能而减少,打开网页的速度比较快。

什么时候使用字节流、什么时候使用字符流

在程序中,所有数据都是以流的方式进行传输或者保存的,程序需要数据的时候要使用输入流来读取数据,需要保存的时候,用输出流完成。而流指的就是比特流,即01串。

字节流即,InputStream和OutputSteam,这两个抽象类是所有字节流的父类。

字符流即,Reader和Writer,这两个抽象类是所有字符流的父类。

我们知道所有的文件归根结底还是以01串即字节的形式存储的,无论是什么文件,视频、音频、文本等等,所以我们可以通过字节流的形式去处理文件。但是处理文本文件时,字节流可能会出现乱码的情况(具体是因为中文文字,并非一个字节UniCode字符集中,中文是2个字节,UTF-编码规则中,中文则会被编成3个字节)很难避免出现一个字的字节被分开,就会出现乱码。

在加上处理的文件其实很大一部分是文本文件,所以字符流出现了,字符流是一种专门处理文本文件的流。

字节流和字符流的主要区别是什么?

  1. 字节流在操作时不会用到缓冲区,是直接对文件本身进行操作。而字符流在操作时使用了缓冲区,通过缓冲区再操作文件(如果一个程序对频繁一个资源进行IO操作,效率会非常低,此时,通过缓冲区,先把需要操作的数据暂时放入内存中,以后直接从内存中读取数据,可以避免多次IO操作,提高效率)
  2. 再硬盘上所有文件都是以字节的形式存在的(图片,声音,视频),而字符值再缓冲区中才会形成(真正存储和传输数据时都是以字节为单位的,字符只是存在与内存当中的,所以,字节流使用范围更广)

Java序列化中如果有些字段不想进行序列化怎么办

对于不想进行序列化的变量,使用transient关键字修饰。

transient关键字的作用是,被transient修饰的字段,再序列化的过程中,会被排除在外;当对象被反序列化时,transient修饰的变量值不会恢复和持久化。transient只能用于修饰变量,不能修饰类和方法。

获取用键盘输入常用的两种方法

方法1、通过Scanner

Scanner input = new Scanner(System.in);
String s=input.nextLine();

方法2、通过BufferedReader

BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String s=br.readLine();

泛型

什么是泛型?有什么作用

Java泛型是JDK5中引入的一个新特性。使用泛型参数,可以增强代码的可读性以及稳定性。

泛型是一种编译时安全检查机制,编译器会对泛型参数进行检测,并且通过泛型参数可以指定传入的对象类型,例如指定了泛型的集合对象,你传入非泛型参数的数据,就会报出编译时异常。

原生集合对象中存储的是Object对象,取出对象时只能是Object,需要类型转换才能得到想要的数据类型,使用泛型,可以自动转换数据类型。

泛型的使用方法

泛型有三种使用方法

  1. 泛型方法  public void <T> T test(T t){}
  2. 泛型接口  interface Test<T>{}
  3. 泛型类 class Test<T>{}

项目中哪里用到了泛型

  1. 基本大部分集合容器的使用都用到了泛型,我再实现简易Spring框架的IOC容器时,就设置了一个ConcurrentHashMap<Class<?>,Object>
  2. 自定义通用返回结果时,我们返回数据给前端,格式一般是Json的                                        {code:200,msg="",data={}},data属性是不确定的,这种时候就会用到泛型

类型擦除了解吗

泛型是一种编译时类型检查机制,他到运行时就不起作用了。也就是编译完的字节码文件中,是没有泛型信息的。运行时泛型信息消失了,这就是类型擦除。

因为这种泛型擦除机制的存在,我们可以通过反射机制,再一个集合容器中存储非指定泛型的数据类型。

注解

什么是注解

注解是JDK5引入的新特性,可以看成是一种特殊的注释,一般来说注解是没有用的,比如Java本身自带的@Override注解,他只能告诉你,这个方法是一个重写的方法。对方法本身并没有影响。

注解是Java提供的一种对元程序中元素关联信息和元数据的方法。

注解可以作用于,类,接口,方法,变量。                                                                                      注解的申明方式是 @integerface,他本质上是一个继承了Annotation接口的接口。                            注解再运行时,java会通过动态代理创建一个继承了Proxy类,实现了Annotation接口的接口,        Annotation接口提供一下四个抽象方法

public interface Annotation {
     boolean equals(Object var1);
     int hashCode();
     String toString();
     Class<? extends Annotation> annotationType();
 }

这也是为什么注解中申明一个属性是以方法的模式 即 public String say() default "nn";

代理类会实现包括Annotation接口抽象方法和属性方法的所有方法。

之前我们说了,注解只能起到一个注释的作用,但是实际上,注解和反射一起使用时,注解会成为一种非常强大的功能。

注解的解析方法有哪几种

注解只有被解析之后才会生效,常见的解析方法有两种

  • 编译器直接扫描:编译器再编译Java代码的时候扫描对于的注解并处理,比如某个方法使用@Override注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。
  • 运行时通过反射处理:像框架中自带的注解,比如Spring框架中的@Value,@Service等都是通过反射来处理的。

元注解

可以理解成注解的注解,即注解的元数据

有四种

  • @Target 用于表示这个注解可以修饰的范围,类、方法、变量、接口等等
  • @Retention 用于表示该注解保留时间的长短,需要在什么级别保留注解信息,编译时,运行时等
  • @Documented 用于描述-JavaDoc,被改注解标注的注解,生成API文档时会存在
  • @Inherited 这个注解标注的注解被用于一个类时,继承了这个类的子类可以获得父类的该注解,如果没有标注,则不能获取。

Java异常架构与异常关键字

Java异常简介

Java异常是Java提供的一种识别及响应错误的一致性机制。

Java异常机制可以使用程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高了程序健壮性。在有效使用异常的情况下,异常能清晰的回答what,where,why这三个问题即异常类型回答了什么被抛出,异常堆栈跟踪回答了在哪抛出,异常信息回答了为什么抛出

JAVA程序运行中GBK java中gbk是什么意思_泛型

1、Throwable(可抛出的)

Throwable是Java语言中所有错误与异常的父类

Throwable包括两个子类,Error(错误)、Execption(异常),他们通常用于指示发生了异常情况。

Throwable包含了其线程创建时线程执行堆栈的快照,他提供了pringStackTrace()等接口用于获取堆栈跟踪信息。

2、Error(错误)

定义:Error类及其子类,是程序中无法处理的错误,表示运行应用程序出现了严重错误。

特点:此类错误一般表示代码运行时JVM出现问题。通常有Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等,比如OutOfMemoryError(内存不足异常),StackOverflowError(栈溢出错误)。此类错误发生时,JVM将终止线程。这些错误是不受检查异常,非代码错误。因此此类错误发生时,应用程序不应该去处理此类错误。按照Java惯例,我们不应该实现任何新的Error子类。

3、Execption(异常)

定义:程序本身可以捕获并处理的异常。Exception这种异常又分为两类:运行时异常和编译时异常。  

运行时异常

定义:RuntimeException类及其子类,表示在JVM运行期间可能出现的异常。

特点:Java编译器不会检查它,也就是说,当程序中可能出现这类异常时,倘若既“没用通过throws声明抛出它”,也“没有用try-catch语句捕获它”,还是会编译通过。比如NullPointException空指针异常、ArrayIndexOutBoundException数组下标越界异常、ClassCastException类型转换异常、ArithmeticException算数异常。此类异常属于不受检查异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。虽然Java编译器不会检查运行时异常,但是我们也可以通过throws进行声明抛出,也可以通过try-catch对他进行捕获处理。如果产生运行时异常,则需要通过修改代码来进行避免。

RuntimeException异常会由Java虚拟机自动抛出并捕获(就算我们没写异常捕获语句运行时也会抛出错误),此类异常的出现绝大多数情况时代码本身有有问题应该从逻辑上去解决并改进代码。

编译时异常

定义:Exception中除RuntimeException及其子类之外的异常

特点:Java编译器会检查它,如果程序中出现异常,比如ClassNotFoundException(没有找到指定类型异常),IOException(IO流异常),要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。在程序中,通常不会自定义该类的异常,而是直接使用系统提供的异常类。该异常我们必须手动在代码中添加捕获语句来处理该异常。

4、受检异常与非受检异常

Java的所有异常可以分为受检异常(checked exception)和非受检异常(unchecked exceptiong)

受检异常

编译器必须要处理的异常。正常的程序在运行过程中,经常出现、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除RuntimeException及其子类外,其他的Exception异常都属于受检异常。编译器会检查此类异常,提示你处理本类异常。

非受检异常

编译器不会进行检查,并且必须处理的异常,也就说当程序中出现此类异常时,即使我们没有捕获他,也没有抛出,编译也会通过,该类异常包括运行时异常(RuntimeException及其子类)和错误(Error)

Error 和 Exception 区别是什么

Error类型叫做错误,这种错误一般是虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等,编译器不会对这类错误进行检测,Java应用程序也不应对这类错误进行捕获,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复;

Exception类的异常是可以在应用程序中进行捕获并处理的,通常遇到这种错误,应该对其进行处理,使应用程序可以正常运行。

运行时异常和一般异常区别是什么

运行时异常指RuntimeException及其子类,其他的继承Exception的异常即是一般异常。

运行时异常和一般异常的区别是,运行时异常不会被编译器检测,只有在运行时,才会出现异常,编译器不会强制要求异常处理。

JVM是如何处理异常的

在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并转交给JVM的过程叫做抛出异常。可能有一系列的方法调用,最终进入抛出异常的方法,这一系列方法调用的有序列表叫做调用栈。

JVM会顺着调用栈去查看是否有可以处理异常的代码,如果有,则调用异常处理代码。当JVM发现可以处理异常的代码时,会把发生的异常传递给他。如果JVM没有找到可以处理该异常的代码块,JVM就会将该异常转交给默认的异常处理器(默认为JVM的一部分),默认异常处理器打印出异常信息并终止应用程序。

throw和throws的区别是什么

throw关键字是写在方法或者方法块中的,用于抛出一类方法,而throws声明在方法后面,用于抛出这个类中可能出现的所有该类异常,调用throws异常的方法本身也需要处理异常或是抛出异常。

NoClassDefFoundError和ClassNotFoundException的区别

NoClassDefFoundError是一个错误,程序本身无法处理,发生这种错误一般是编译后的文件被删除,运行时无法找到对应文件,导致虚拟机系统发生错误。

ClassNotFoundException是一个编译时异常,需要显示的try-catch捕获处理或者用throw/throws抛出,这种异常一般在反射时,使用Class.forName(),ClassLoader.loadClass()等时候,动态加载类到内存中的时候,通过该路径没有找到该类,就会抛出该异常;另一种抛出该异常的可能是这个类已经被某个类加载器加载过了,另一个加载器又尝试去加载他。

try-catch-finally中哪个部分可以省略

catch和finally可以省略其中一个,格式上是这样设置的,但是在实际编码过程中,一般是不进行省略的。

catch可以省略的情况是,try中出现的是运行时异常,这种异常编译器检测不到,所以不强制进行捕获。但是这种情况到运行时会报错。当try块中的异常时编译时异常,编译器是可以检测到的,强制进行catch处理。

finally可以省略的情况,发生异常,也就是程序接下来的流程无法正常进行时,没有要进行的异常后操作就可以不用finally块。

try-catch-finally中如果catch中return了,finally还会执行吗

只要程序运行到try块的代码了,finally无论如何都会执行,除非你在catch中将JVM关闭。

注意:在finally中进行return操作是不好的,我们知道return操作可以直接结束当前方法,正常来说,没有发生异常就在try块中return,发生异常在catch中return,但是如果存在finally块,方法会在return前,运行finally块,假设finally块中存在return语句,就会直接结束方法,也就是try和catch中的return被覆盖了,如果此时finally块return的结果是错误的,就是非常严重的错误了。

假设我们在finally块中不进行return,而是把将要return的值改变呢?

假设发生异常,catch块中

{
a=20;
return a;
}
finally中
{
a=30;
}

此时return的结果依然是 a=20;finally不是会在return前运行吗?,为什么a没有改变,这是因为Java是一个值传递的语言,此时传入finally中的参数是a的值的复制,即使改变了也只是改变了finally块中的局部变量的a,catch块中的a=20是没有改变了。

假设传入的是一个对象引用,我们此时就可以改变这个对象了,但是不能让catch块中对象引用指向的对象改变。

常见的运行时异常有哪些

  • IndexOutOfBoundException
  • NullPointException
  • ClassCastException
  • NumberFormatException
  • ArithmeticException

Java序列化(创建可复用的Java对象)

序列化的作用就是保存对象到磁盘或者内存中,实现一个复用的作用。

Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在。即,这些对象的生命周期不会超过JVM的生命周期。但是在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。序列化对于面向对象编程语言是非常重要的,我们知道无论是什么编程语言,IO操作还是操作系统来帮忙完成的,而底层的IO实际上是以字节流的形式传输的。所以当我们需要进行对象传输时,序列化操作就不可避免了,同样,将字节流转换为对于语言的数据结构,即反序列化也必不可少。

Java为什么要实现序列化对象

  • 保存对象,对象会随着JVM的关闭而释放,我们需要JVM关闭后仍然可以保存的对象
  • 对象传输,无论什么语言,进行IO操作实际是靠操作系统进行,操作系统传输数据就是传输字节流,所以我们需要将对象序列化。

Java对象序列化就能够帮助我们实现该功能。

序列化对象以字节数组保持,静态成员不保存(因为静态成员是属于类的,我们序列化的对象是实例对象)

使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。对象序列化保存的是对象的状态,即它的成员变量。由此可知,对象序列化不会关注类中的静态变量。

Serializable实现序列化

再Java中,只要一个类实现了java.io.Serializable接口,那么它就可以被序列化。

ObjectOutputStream 和ObjectInputStream对对象进行序列化及其反序列化

writeObject方法和readObject方法自定义序列化策略

序列化ID

虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化ID是否一致。

序列化子类时,想要保存父类数据,父类也得实现Serializable接口。

有些私密数据不想参与序列化与反序列化用transient修饰(transient只可用于变量)

实际开发中有哪些用到序列化和反序列化的场景

  1. 对象再进行网络传输(比如远程方法调用RPC的时候)之前需要先被序列化,接收到序列化的对象之后再进行反序列化
  2. 对象的存储,将对象存储到文件中,需要序列化,从文件中读取数据需要反序列化。
  3. 对象存储到缓存数据库(如redis)时需要用到序列化,将对象从缓存数据库中读取出来需要反序列化。

*序列化协议属于TCP/IP应用层的一部分

Java8十大新特性详解

一、接口的默认方法

Java8允许我们给接口添加一个非抽象的方法实现,只需要使用default关键字即可,这个特征又叫做扩展方法。

JAVA程序运行中GBK java中gbk是什么意思_Java_02

实现了testInterface的实现类,只需要重写getName方法即可。

二、Lambda表达式

Java8提供的更简洁的语法

JAVA程序运行中GBK java中gbk是什么意思_序列化_03

三、函数式接口

函数式接口即,只声明了一个抽象的接口,将该接口作为方法的参数,传参时用lambda表达式作为参数。

声明为函数式接口的接口要标注@FunctionalInterface接口,标注该接口的接口不能声明第二个抽象方法。

四、方法与构造函数引用

 当需要传递给Lambda体的操作已经又方法实现了,我们就可以使用方法引用

JAVA程序运行中GBK java中gbk是什么意思_泛型_04

JAVA程序运行中GBK java中gbk是什么意思_泛型_05

JAVA程序运行中GBK java中gbk是什么意思_Java_06

 

上图即为方法引用。TestInterface接口需要实现getName方法,再TestInterfaceImpl类中,我们实现了getName的功能,此时(a)->TestInterfaceImpl.getName(a)就可以被替换为Test InterfaceImpl::getName;

构造器同样也可以这样使用

 

JAVA程序运行中GBK java中gbk是什么意思_泛型_07

JAVA程序运行中GBK java中gbk是什么意思_java_08

 

JAVA程序运行中GBK java中gbk是什么意思_序列化_09

 

 Person::new,即为构造器引用。

五、多重注解

多重注解即,同一个注解可以再一个对像上标注多次,我们只需要再注解上标注@Repeatable元注解即可。