美文网首页
Java基础面试准备

Java基础面试准备

作者: Kraos | 来源:发表于2019-08-13 11:25 被阅读0次
    • HashMap不保证数据有序,LinkedHashMap保证数据可以保持插入顺序,而如果我们希望Map可以保持key的大小顺序的时候,我们就需要利用TreeMap了

    • TreeMap使用的是红黑树,树具有不错的平衡性,这样操作的速度就可以达到log(n)的水平了。

    • 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家Peter.J.Landin发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。

    • Java中最常用的语法糖主要有泛型、变长参数、条件编译、自动拆装箱、内部类等。虚拟机并不支持这些语法,它们在编译阶段就被还原回了简单的基础语法结构,这个过程成为解语法糖。

      泛型出来前List写法存在的问题,由于List里面的内容是Object类型的,自然任何对象类型都可以放入、都可以取出,但是这么写会有两个问题:

      1、当一个对象放入集合时,集合不会记住此对象的类型,当再次从集合中取出此对象时,该对象的编译类型变成了Object。

      2、运行时需要人为地强制转换类型到具体目标,实际的程序绝不会这么简单,一个不小心就会出现java.lang.ClassCastException。

      这有点像我把所有食物放进榨汁机炸成了碎然后放进冰箱,第二天面对冰箱里一盒盒的食物碎我必须想起来他们原来是啥我才能拿到我吃到我想吃的番茄牛肉。

    • 泛型是对Java语言类型系统的一种扩展,有点类似于C++的模板,可以把类型参数看作是使用参数化类型时指定的类型的一个占位符。

    • 泛型使得类型错误在编译器能够捕获,而不是在运行时当作java.lang.ClassCastException展示出来,消除了代码中的强制类型转换。

    相对于前者,泛型相当于我把碎块的食物放进冰箱前打好了标签记住它原来是什么,这样我就不用靠人脑去记它原来是什么食物了,第二天可以轻松找到我想吃的东西。

    • 泛型的实质:允许在定义接口、类时声明类型形参,类型形参在整个接口、类体内可当成类型使用,几乎所有可使用普通类型的地方都可以使用这种类型形参

    • 在JDK 1.7 增加了泛型的“菱形”语法:Java允许在构造器后不需要带完成的泛型信息,只要给出一对尖括号(<>)即可,Java可以推断尖括号里应该是什么泛型信息。

      Container<String,String> c1=new Container<>("name","hello");
      Container<String,Integer> c2=new Container<>("age",22);
      
    • 当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或者从该父类派生子类,需要注意:使用这些接口、父类派生子类时不能再包含类型形参,需要传入具体的类型,又或者直接不带形参指定。

    • 泛型方法就是在声明方法时定义一个或多个类型形参。

    • 泛型构造器和普通泛型方法没区别,一种是显式指定泛型参数,另一种是隐式推断,如果是显式指定则以显式指定的类型参数为准,如果传入的参数的类型和指定的类型实参不符,将会编译报错。

      public static void main(String[] args){
              //隐式
              new Person(22);
              //显示
              new<String> Person("hello");
      }
      
    • 类型通配符就是匹配任何类型的类型实参,问号(?)被成为通配符,它的元素类型可以匹配任何类型

    • 上限通配符限制使用泛型类别,只能用某个特定类型或者是其子类型才能实例化该类型

      //它表示集合中的所有元素都是Shape类型或者其子类
      List<? extends Shape>
      
    • 下限通配符限制使用泛型类别,只能用某个特定类型或者是其父类型才能实例化该类型

      //它表示集合中的所有元素都是Circle类型或者其父类
      List <? super Circle>
      
    • 不管为泛型的类型形参传入哪一种类型实参,对于Java来说,它们依然被当成同一类处理,在内存中也只占用一块内存空间。从Java泛型这一概念提出的目的来看,其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

    • 由于系统中并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类。

    • 反射是在运行状态知晓该类的属性和方法,并且调用,生成动态代理

    • 应用场景:逆向代码,反编译;与注解结合->Retrofit;单纯的反射机制应用->EventBus;动态生成类框架 Gson

    • 获得Class对象

      //第一种方式 通过Class类的静态方法——forName()来实现
      class1 = Class.forName("com.lvr.reflection.Person");
      //第二种方式 通过类的class属性
      class1 = Person.class;
      //第三种方式 通过对象getClass方法
      Person person = new Person();
      Class<?> class1 = person.getClass();
      
    • 获得成员变量

      Field[] allFields = class1.getDeclaredFields();//获取class对象的所有属性
      Field[] publicFields = class1.getFields();//获取class对象的public属性
      Field ageField = class1.getDeclaredField("age");//获取class指定属性
      Field desField = class1.getField("des");//获取class指定的public属性
      
    • 获得对象的方法

      Method[] methods = class1.getDeclaredMethods();//获取class对象的所有声明方法
      Method[] allMethods = class1.getMethods();//获取class对象的所有public方法 包括父类的方法
      Method method = class1.getMethod("info", String.class);//返回次Class对象对应类的、带指定形参列表的public方法
      Method declaredMethod = class1.getDeclaredMethod("info", String.class);//返回次Class对象对应类的、带指定形参列表的方法
      
    • 获得对象的构造函数

      Constructor<?>[] allConstructors = class1.getDeclaredConstructors();//获取class对象的所有声明构造函数
      Constructor<?>[] publicConstructors = class1.getConstructors();//获取class对象public构造函数
      Constructor<?> constructor = class1.getDeclaredConstructor(String.class);//获取指定声明构造函数
      Constructor publicConstructor = class1.getConstructor(String.class);//获取指定声明的public构造函数
      
    • 代理模式指客户不直接操控原对象,通过代理对象间接地操控原对象

    • 在代理类的构造函数中实例化一个目标对象,然后调用目标对象的行为接口

    • 代理模式好处在于在某些模块调用前后加上统一的前后处理,比如修改订单前需要统一加上登陆验证和日志记录处理

    • 代理模式分为静态代理和动态代理

    • 动态代理的思路是通过反射实现,运行时候动态的生成一个代理类

      注:这句还不是很理解->代理类的字节码将在运行时生成并载入当前代理的 ClassLoader

    • java.lang.reflect.Proxy是生成代理类的主类,通过 Proxy 类生成的代理类都继承了 Proxy 类。Proxy提供了用户创建动态代理类和代理对象的静态方法,它是所有动态代理类的父类。

    • java.lang.reflect.InvocationHandler:当调用动态代理类中的方法时,将会直接转接到执行自定义的InvocationHandler中的invoke()方法。即我们动态生成的代理类需要完成的具体内容需要自己定义一个类,而这个类必须实现 InvocationHandler 接口,通过重写invoke()方法来执行具体内容。

    • Class类的泛型,例:Class<String>,如果对应的类暂时未知,则使用Class<?>(?是通配符)。

    • Class的泛型能够有效避免类型转换,防止出现ClassCastException

    • 编译时不会出现任何问题,在运行时就会抛出ClassCastException

      String string = (String) ObjectFactory.getInstance("java.util.Date");
      

      使用泛型就可以比避免上述问题

    • 注解Annotation是Java平台的元数据,元数据是关于数据的数据,它是添加到程序的额外信息(1.5之后的JDK)

    • 元数据作用:

      1.编写文档:根据注释创建文档

      2.编译检查:声明是否编译期检测

      3.代码分析:如方法重载

    • Java的注解功能通过反射实现,以编程方式访问元数据注释,为程序元素附加额外数据

    • Annotaion无论增加还是删除都并不影响程序代码的正常执行,如果希望它起作用,要通过解析工具或编译工具对Annotation中的信息进行解析和处理。

    • Java提供了多种内建的注解,主要是为了实现编译检查

    • @Override告知编译器需要覆写父类的方法,如果某方法带有该注解但并没有覆写超类相应的方法,则编译器会生成错误信息。如果父类没有这个要覆写的方法,则编译器也会生成一条错误信息。(有点像平安符的作用)

    • @Deprecated告知编译器某远古方法或成员变量已经过时,不适合使用

    • @SuppressWarnings告知编译器忽略特定的警告信息,适合用于除注解类型声明和包名之外的所有元素

    • 该注解有方法value(),可支持多个字符串参数,用户指定忽略哪种警告,例如:

      @SupressWarning(value={"uncheck","deprecation"})
      
      img
    • @FunctionalInterface告知编译器去检查该接口,保证它是函数式接口,即只能包含一个抽象方法,否则就会编译出错。

    • 在java.lang。annotation包下提供了6个Meta Annotation(元Annotataion)

    • 自定义Annotation只能通过@interface的方式,这样做会隐含地继承Annotation

      @Documented
      @Target(ElementType.METHOD)
      @Inherited
      @Retention(RetentionPolicy.RUNTIME)
      public @interface MyAnnotataion{
          String name();
          String website() default "hello";
          int revision() default 1;
      }
      
    • 通过反射技术可以解析自定义注解

    • @Documented 被该元Annotation修饰的Annotation类将会被javadoc工具提取成文档

    • 定义其他Annotation类时如果使用了@Documented修饰,则所有使用该Annotation修饰的程序元素的API文档中将会包含该Annotation说明。

    • @Inherited令修饰的Annotation具有继承性,即使用该注解修饰的类,其子类将自动被@Xxx修饰。

    • @Retention表示该注解类型的注解保留的时长,默认保留策略为RetentionPolicy.CLASS

      img
    • @Target 表示该注解类型的所适用的程序元素类型,默认是所有程序元素适用

    • 范围修饰符的范围:public > protected>default>private

    • AWT->Swing 后者是进阶版,在不同系统保持相同的风格

    • 一个类只要有一个抽象方法就要声明为抽象类

    • 软引用在内存不够时才回收,弱引用是只要GC启动了就回收

      题外话:在JDBC编程模式中,一个数据库连接建立时,就处于一个自动提交模式,每一个SQL语句被执行完成后就会被自动提交,反映到数据库中。当需要把几条逻辑上相关的SQL组成的一个事务执行时,就需要关闭事务自动提交模式。如下面的语句所示: con.setAutoCommit(false); // 关闭自动提交模式 一旦关闭了事务自动提交模式,不会有任何SQL语句被提交至数据库系统执行,除非显式的调用提交方法。

    • JVM在判定两个class是否相同时,不仅要判断两个类名是否相同,而且要判断是否由同一个类加载器实例加载的。

    • 当输入过程中意外到达文件或流的末尾时,抛出EOFException异常,正常情况下读取到文件末尾时,返回一个特殊值表示文件读取完成,例如read()返回-1表示文件读取完成。

    • 垃圾回收是jvm自动进行的,可以调用System.GC();提醒jvm,但是具体什么时间不确定。

    • 类方法是指类中被static修饰的方法,无this指针。

    • java支持分布式计算

    • 调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。

    • super()和this()类似,区别是,super从子类中调用父类的构造方法,this()在同一类内调用其它方法。

    • 向上转型具有传递性 诸如a派生b b派生c之后的多态实现是成立的

    • 重载是编译期间的活动,重写是运行期间的活动。

      jar 将许多文件组合成一个jar文件

      javac 编译

      javadoc 它从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档。

      javah 把java代码声明的JNI方法转化成C\C++头文件。

    • switch结构中没有break;switch case 方法中若,没有break跳出执行则程序就会从第一个匹配上的case一直执行到整个结构结束。

      1. List 是一个有序集合,可以存放重复的数据 (有序:存进是什么顺序,取出时还是什么顺序)
        (1).ArrayList 底层是数组适合查询,不适合增删元素。
        (2).LiskedList 底层是双向链表适合增删元素,不适合查询操作。
        (3).Vector 底层和ArrayList相同,但是Vector是线程安全的,效率较低很少使用
      2. Set 是一个无序集合,不允许放重复的数据 (无序可重复,存进和取出的顺序不一样)
        (1).HashSet 底层是哈希表/散列表
        (2).TreeSet 继承sartedSet接口(无需不可重复,但存进去的元素可以按照元素的大小自动排序)
      3. Map 是一个无序集合,以键值对的方式存放数据,键对象不允许重复,值对象可以重复。
        (1).HashMap实现不同步,线程不安全。 HashTable线程安全
        (2).HashMap中的key-value都是存储在Entry中的。
        (3).HashMap可以存null键和null值,不保证元素的顺序恒久不变,它的底层使用的是数组和链表,通过hashCode()方法和equals方法保证键的唯一性
    • 字符用单引号,字符串用双引号,与引号中的内容无关

    • static修饰的变量称为静态变量,静态变量属于整个类,而局部变量属于方法,只在该方法内有效,所以static不能修饰局部变量

    • 数组元素中默认有一个空字符串

    • []只能匹配一个既定字

    • HashTable 和 ConcurrentHashMap 都是线程安全的。区别在于他们对加锁的范围不同,HashTable 对整张Hash表进行加锁,而ConcurrentHashMap将Hash表分为16桶(segment),每次只对需要的桶进行加锁。

    • 在java 中,声明一个数组时,不能直接限定数组长度,只有在创建实例化对象时,才能对给定数组长度.。

    • 与OutputStream、Writer等输出流不同的是,RandomAccessFile允许自由定义文件记录指针,即不从开始的地方开始输出,因此RandomAccessFile可以向已存在的文件后追加内容。如果程序需要向已存在的文件后追加内容,则应该使用RandomAccessFile。

    • RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传。

      "r": 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
      "rw": 打开以便读取和写入。
      "rws": 打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。
      "rwd" : 打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。

    • /** 
       * 测试利用多线程进行文件的写操作 
       */  
      public class Test {  
      
          public static void main(String[] args) throws Exception {  
              // 预分配文件所占的磁盘空间,磁盘中会创建一个指定大小的文件  
              RandomAccessFile raf = new RandomAccessFile("D://abc.txt", "rw");  
              raf.setLength(1024*1024); // 预分配 1M 的文件空间  
              raf.close();  
      
              // 所要写入的文件内容  
              String s1 = "第一个字符串";  
              String s2 = "第二个字符串";  
              String s3 = "第三个字符串";  
              String s4 = "第四个字符串";  
              String s5 = "第五个字符串";  
      
              // 利用多线程同时写入一个文件  
              new FileWriteThread(1024*1,s1.getBytes()).start(); // 从文件的1024字节之后开始写入数据  
              new FileWriteThread(1024*2,s2.getBytes()).start(); // 从文件的2048字节之后开始写入数据  
              new FileWriteThread(1024*3,s3.getBytes()).start(); // 从文件的3072字节之后开始写入数据  
              new FileWriteThread(1024*4,s4.getBytes()).start(); // 从文件的4096字节之后开始写入数据  
              new FileWriteThread(1024*5,s5.getBytes()).start(); // 从文件的5120字节之后开始写入数据  
          }  
      
          // 利用线程在文件的指定位置写入指定数据  
          static class FileWriteThread extends Thread{  
              private int skip;  
              private byte[] content;  
      
              public FileWriteThread(int skip,byte[] content){  
                  this.skip = skip;  
                  this.content = content;  
              }  
      
              public void run(){  
                  RandomAccessFile raf = null;  
                  try {  
                      raf = new RandomAccessFile("D://abc.txt", "rw");  
                      raf.seek(skip);  
                      raf.write(content);  
                  } catch (FileNotFoundException e) {  
                      e.printStackTrace();  
                  } catch (IOException e) {  
                      // TODO Auto-generated catch block  
                      e.printStackTrace();  
                  } finally {  
                      try {  
                          raf.close();  
                      } catch (Exception e) {  
                      }  
                  }  
              }  
          }  
      
      }
      
    • JAVA的内存管理比C++方便,而且错误处理也比较好;C++的速度比JAVA快。
      C++更适用于有运行效率要求的情况,JAVA适用于效率要求不高,但维护性要好的情况。

    • img
    • 使用try...catch块捕获时可以没有catch块,但当没用catch块的时候必须得有finally块

    • 数值型变量在默认情况下为Int型,byte和short型在计算时会自动转换为int型计算

    • TestSuper和Date的getClass都没有重写,他们都是调用Object的getClass,而Object的getClass作用是返回的是运行时的类的名字。这个运行时的类就是当前类,所以

    • img
    • Java NIO是一种新式的IO标准,标准的IO是基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入通道也类似。

    • FileChannel用于文件的数据读写。 DatagramChannel用于UDP的数据读写。 SocketChannel用于TCP的数据读写。 ServerSocketChannel允许我们监听TCP链接请求,每个请求会创建会一个SocketChannel。

    • 类方法中不能引用对象变量、不能调用类的对象方法、不能使用super、this关键字、类方法不能被覆盖。

    • switch语句后的控制表达式只能是short、char、int、long整数类型和枚举类型,不能是float,double和boolean类型。String类型是java7开始支持。

    • Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。

    • 只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句

    • throws是用于声明抛出的异常,而throw是用于抛出异常。

    • 当资源不足、约束失败、或是其它程序无法继续运行的条件发生时,就产生错误。程序本身无法修复这些错误的。例如,VirtualMachineError就属于错误。

    相关文章

      网友评论

          本文标题:Java基础面试准备

          本文链接:https://www.haomeiwen.com/subject/bujvjctx.html