Java
基础
说说自定义注解的场景及实现
利用自定义注解,结合SpringAOP可以完成权限控制、日志记录、统一异常处理、数字签名、数据加解密等功能。
实现场景(API接口数据加解密)
1)自定义一个注解,在需要加解密的方法上添加该注解
2)配置SringAOP环绕通知
3)截获方法入参并进行解密
4)截获方法返回值并进行加密
说一下泛型原理,并举例说明
==泛型就是将类型变成参数传入,使得可以使用的类型多样化,从而实现解耦。===Java 泛型是在 Java1.5 以后出现的,为保持对以前版本的兼容,使用了擦除的方法实现泛型。擦除是指在一定程度无视类型参数 T,直接从 T 所在的类开始向上 T 的父类去擦除,如调用泛型方法, 传入类型参数 T 进入方法内部,若没在声明时做类似 public T methodName(T extends Father t){},Java 就进行了向上类型的擦除,直接把参数 t 当做 Object 类来处理,而不是传进去的 T。即在有泛型的任何类和方法内部,它都无法知道自己的泛型参数,擦除和转型都是在边界上发生,即传进去的参在进入类或方法时被擦除掉,但传出来的时候又被转成了我们设置的 T。在泛型类或方法内,任何涉及到具体类型(即擦除后的类型的子类)操作都不能进行,如 new T(),或者 T.play()(play 为某子类的方法而不是擦除后的类的方法)
说说你对 Java 注解的理解
注解是通过@interface 关键字来进行定义的,形式和接口差不多,只是前面多了一个@
使用时@TestAnnotation 来引用,要使注解能正常工作,还需要使用元注解,它是可以注解到注解上的注解。元标签有@Retention @Documented @Target @Inherited@Repeatable 五种
@Retention 说明注解的存活时间,取值有 RetentionPolicy.SOURCE 注解只在源码阶段保留, 在编译器进行编译时被丢弃;RetentionPolicy.CLASS 注解只保留到编译进行的时候,并不会被加载到 JVM 中。RetentionPolicy.RUNTIME 可以留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
@Documented 注解中的元素包含到 javadoc 中去
@Target 限 定 注 解 的 应 用 场 景 , ElementType.FIELD 给 属 性 进 行 注解 ; ElementType.LOCAL_VARIABLE 可以给局部变量进行注解;ElementType.METHOD可以给方法进行注解;ElementType.PACKAGE 可以给一个包进行注解 ElementType.TYPE可以给一个类型进行注解,如类、接口、枚举
@Inherited 若一个超类被@Inherited 注解过的注解进行注解,它的子类没有被任何注解应用的话,该子类就可继承超类的注解;
注解的作用:
1)提供信息给编译器:编译器可利用注解来探测错误和警告信息
2)编译阶段:软件工具可以利用注解信息来生成代码、html 文档或做其它相应处理;
3)运行阶段:程序运行时可利用注解提取代码
注解是通过反射获取的,可以通过 Class 对象的 isAnnotationPresent()方法判断它是否应用了某个注解,再通过 getAnnotation()方法获取 Annotation 对象
谈谈你对解析与分派的认识?
解析指方法在运行前,即编译期间就可知的,有一个确定的版本,运行期间也不会改变。解析是静态的,在类加载的解析阶段就可将符号引用转变成直接引用。
分派可分为静态分派和动态分派,重载属于静态分派,覆盖属于动态分派。静态分派是指在 重载时通过参数的静态类型而非实际类型作为判断依据,在编译阶段,编译器可根据参数的 静态类型决定使用哪一个重载版本。动态分派则需要根据实际类型来调用相应的方法。
讲一下常见编码方式?
编码的意义:计算机中存储的最小单元是一个字节即 8bit,所能表示的字符范围是 255个, 而人类要表示的符号太多,无法用一个字节来完全表示,固需要将符号编码,将各种语言翻译成计算机能懂的语言。
1)ASCII 码:总共 128 个,用一个字节的低 7 位表示,0?31 控制字符如换回车删除等;32~126 是打印字符,可通过键盘输入并显示出来;
2)ISO-8859-1,用来扩展 ASCII 编码,256 个字符,涵盖了大多数西欧语言字符。
3)GB2312:双字节编码,总编码范围是 A1-A7,A1-A9 是符号区,包含 682 个字符,B0-B7 是汉字区,包含 6763 个汉字;
4)GBK 为了扩展 GB2312,加入了更多的汉字,编码范围是 8140~FEFE,有 23940 个码位,能表示 21003 个汉字。
5)UTF-16: ISO 试图想创建一个全新的超语言字典,世界上所有语言都可通过这本字典Unicode 来相互翻译,而 UTF-16 定义了 Unicode 字符在计算机中存取方法,用两个字节来表示 Unicode 转化格式。不论什么字符都可用两字节表示,即 16bit,固叫 UTF-16。
6)UTF-8:UTF-16 统一采用两字节表示一个字符,但有些字符只用一个字节就可表示,浪费存储空间,而 UTF-8 采用一种变长技术,每个编码区域有不同的字码长度。 不同类型的字符可以由1~6个字节组成。
utf-8 编码中的中文占几个字节;int 型几个字节?
utf-8 是一种变长编码技术,utf-8 编码中的中文占用的字节不确定,可能 2 个、3 个、4个, int 型占 4 个字节。
二叉搜索树和平衡二叉树有什么关系,强平衡二叉树(AVL 树)和弱平衡二叉树(红黑树)有什么区别?
二叉搜索树:也称二叉查找树,或二叉排序树。定义也比较简单,要么是一颗空树,要么就是具有如下性质的二叉树:
(1)若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)任意节点的左、右子树也分别为二叉查找树;
(4)没有键值相等的节点。
平衡二叉树:在二叉搜索树的基础上多了两个重要的特点
(1)左右两子树的高度差的绝对值不能超过 1(度差可以是1,0,-1);
(2)左右两子树也是一颗平衡二叉树。
红黑书:红黑树是在普通二叉树上,对每个节点添加一个颜色属性形成的,需要同时满足以下五条性质
(1)节点是红色或者是黑色;
(2)根节点是黑色;
(3)每个叶节点(NIL 或空节点)是黑色;
(4)每个红色节点的两个子节点都是黑色的(也就是说不存在两个连续的红色节点);
(5)从任一节点到其没个叶节点的所有路径都包含相同数目的黑色节点。
区别:AVL 树需要保持平衡,但它的旋转太耗时,而红黑树就是一个没有 AVL 树那样平衡,因此插入、删除效率会高于 AVL 树,而 AVL 树的查找效率显然高于红黑树。
树和 B+树的区别,为什么 MySQL 要使用 B+树 B 树?
B树:
(1)关键字集合分布在整颗树中;
(2)任何一个关键字出现且只出现在一个结点中;
(3)搜索有可能在非叶子结点结束;
(4)其搜索性能等价于在关键字全集内做一次二分查找;
B+树:
(1)有 n 棵子树的非叶子结点中含有 n 个关键字(b 树是 n-1 个),这些关键字不保存数据,只用来索引,所有数据都保存在叶子节点(b 树是每个关键字都保存数据);
(2)所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针, 且叶子结点本身依关键字的大小自小而大顺序链接;
(3)所有的非叶子结点可以看成是索引部分,结点中仅含其子树中的最大(或最小) 关键字;
(4)通常在 b+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结点;
(5)同一个数字会在不同节点中重复出现,根节点的最大元素就是 b+树的最大元素。
B+树相比于 B 树的查询优势:
(1)B+树的中间节点不保存数据,所以磁盘页能容纳更多节点元素,更“矮胖”;
(2)B+树查询必须查找到叶子节点,B 树只要匹配到即可不用管元素位置,因此 B+树查找更稳定(并不慢);
(3)对于范围查找来说,B+树只需遍历叶子节点链表即可,B 树却需要重复地中序遍历
说说 B-tree 、 B+tree 的区别和使场景?
B-tree:
B-tree 利用了磁盘块的特性进行构建的树。每个磁盘块?个节点,每个节点包含了很关键字。把树的节点关键字增多后树的层级比原来的?叉树少了,减少数据查找的次数和复杂度。
B-tree 巧妙利用了磁盘预读原理,将?个节点的大小设为等于?个页(每页为 4K),这样每个节点只需要?次 I/O 就可以完全载入。
B-tree 的数据可以存在任何节点中。
B+tree:
B+tree 是 B-tree 的变种,B+tree 数据只存储在叶?节点中。这样在 B 树的基础上每个节点存储的关键字数更多,树的层级更少所以查询数据更快,所有指关键字指针都存在叶子节点,所以每次查找的次数都相同所以查询速度更稳定;
数组在内存中如何分配?
静态初始化∶初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度,如∶
//只是指定初始值,并没有指定数组的长度,但是系统为自动决定该数组的长度为 4
String[] computers = f"Del1",“Lenovo”,“Apple”,“Acer”};//
//只是指定初始值,并没有指定数组的长度,但是系统为自动决定该数组的长度为 3
String[] names = new String[]{“多啦 A梦”,“大雄”,“静香”}; //
动态初始化∶初始化时由程序员显示的指定数组的长度,由系统为数据每个元素分配初始值,如∶
//只是指定了数组的长度,并没有显示的为数组指定初始值,但是系统会默认给数组数组
元素分配初始值为 nul1
String[] cars = new String[4]; //
静态初始化方式,程序员虽然没有指定数组长度 ,但是系统已经自动帮我们给分配了 ,而动态初始化方式,程序员虽然没有显示的指定初始化值,但是因为 Java 数组是引用类型的变量,所以系统也为每个元素分配了初始化值 nu11,当然不同类型的初始化值也是不一样的,假设是基本类型 int 类型,那么为系统分配的初始化值也是对应的默认值 0。
Cloneable 接口实现原理
Cloneable 接口是 Java 开发中常用的一个接口,它的作用是使一个类的实例能够将自身拷贝到另一个新的实例中,注意,这里所说的"拷贝"拷的是对象实例,而不是类的定义,进一步说,拷贝的是一个类的实例中各字段的值。
在开发过程中,拷贝实例是常见的一种操作,如果一个类中的字段较多,而我们又采用在客户端中逐字段复制的方法进行拷贝操作的话,将不可避免的造成客户端代码繁杂冗长,而且也无法对类中的私有成员进行复制,而如果让需要具备拷贝功能的类实现 cloneable接口,并重写 clone()方法,就可以通过调用 clone()方法的方式简洁地实现实例拷贝功能
深拷贝(深复制)和浅拷贝(浅复制)是两个比较通用的概念,尤其在 C++语言中,若不弄懂,则会在 delete 的时候出问题,但是我们在这幸好用的是 Java。虽然 ava 自动管理对象的回收,但对于深拷贝(深复制)和浅拷贝(浅复制),我们还是要给予足够的重视,因为有时这两个概念往往会给我们带来不小的困惑。
浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。举例来说更加清楚∶对象 A1 中包含对 B1 的引用,B1 中包含对 c1 的引用。浅拷贝A1得到 A2 ,A2 中依然包含对 B1 的引用,B1 中依然包含对 c1 的引用。深拷贝则是对浅拷贝的递归,深拷贝 A1 得到 A2,A2 中包含对 B2( B1 的 copy )的引用,B2 中包含对 C2( C1 的 copy )的引用。若不对 clone()方法进行改写,则调用此方法得到的对象即为浅拷贝
Java 反射机制
Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为 Java 的反射机制。
Class 类与 java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了 Field,Method,Constructor 类 (每个类都实现了 Member 接口)。这些类型的对象时由 JVM在运行时创建的,用以表示未知类里对应的成员。
这样你就可以使用 Constructor 创建新的对象,用 get()和 set()方法读取和修改与Field 对象关联的字段,用 invoke()方法调用与 Method 对象关联的方法。另外,还可以调用 getFields() getMethods()和 getConstructors()等很便利的方法,以返回表示字段,方法,以及构造器的对象的数组。这样匿名对象的信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。
– 运行结果: 无参构造器 Run………… 有参构造器 Run… Apple
Arrays.sort 和 Collections.sort 实现原理和区别
java.uti1.collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。 java.uti1.collections 是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作。 然后还有混排(Shuffling)、反转(Reverse)、替换所有的元素(fil)、拷贝(copy)、返回 Collections 中最小元素(min)、返回Collections 中最大元素(max)、返回指定源列表中最后一次出现指定目标列表的起始位置( 1astIndexofsubList )、返回指定源列表中第一次出现指定目标列表的起始位置( IndexofSubList )、根据指定的距离循环移动指定列表中的元素(Rotate);事实上 Collections.sort 方法底层就是调用的 array.sort 方法
legacyMergeSort (a)∶归并排序 ComparableTimsort.sortO∶
Timsort 排序:Timsort 排序是结合了合并排序(merge sort)和插入排序(insertion sort)而得出的排序
算法 Timsort 的核心过程
TimSort 算法为了减少对升序部分的回溯和对降序部分的性能倒退,将输入按其升序和降序特点进行了分区。排序的输入的单位不是一个个单独的数字,而是一个个的块-分区。其中每一个分区叫一个 run。针对这些 run 序列,每次拿一个 run 出来按规则进行合并。每次合并会将两个 run 合并成一个 run。合并的结果保存到栈中。合并直到消耗掉所有的run,这时将栈上剩余的 run 合并到只剩一个 run 为止。这时这个仅剩的 run 便是排好序的结果。
综上述过程,Timsort 算法的过程包括
(0)如何数组长度小于某个值,直接用二分插入排序算法
(1)找到各个 run,并入栈
(2)按规则合并 run
Java 获取反射的三种方法
1.通过 new 对象实现反射机制
2.通过路径实现反射机制
3.通过类名实现反射机制
本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕,E-mail:xinmeigg88@163.com
本文链接:http://www.xrbh.cn/tnews/2751.html