1.阅读下面XML文档,然后判断下列说法正确的是( C )。
<company>
<name>TEDU</name>
<address>北京海淀</address>
</company>
A.<company>是元素对象,同样也是文本对象
B.<name>是元素对象,同样也是文本对象
C.北京海淀是文本对象
D.北京海淀是元素对象
2.下列是格式正确的XML标记的是( CD)。
A.<name>张三<name>
B.<name>张三</Name>
C.<name>张三</name>
D.<name/>
3.下列选项中可以解析XML文档的是( BC)。
A.DTD
B.DOM
C.SAX
D.CSS
简答题
1.HashMap扩容原理
JDK1.8HashMap底层采用数组+链表+红黑树形式存储.
如果HashMap初始化的时候没有指定容量,会返回一个空的 Node[] table数组。第一次往HashMap中put(添加)元素的时候 ,会使用默认的参数作为数组的初始化长度:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
当HashMap中的元素数量超过 容量*加载因子 时,会进行扩容操作,容量变为原来的2倍,HashMap的加载因子,默认是0.75:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
2.HashMap底层结构
HashMap底层是一个Node数组,: transient Node<k,V>[] table; (Node<K,V> 类实现了 Map.Entry接口)
当存放元素时,首先会根据Key的hashCode值找到数据应该保存在table数组的下标位置。
如果数组的位置上没有数据 ,直接将这这个键值对保存在该位置上。
如果数组的位置上已经有数据了,即发生了哈希冲突(哈希碰撞),也就是两个对象的key和hash值相等,那么则需要通过Key的equals()方法判断这两个对象是否为同一个对象。如果是,那么原本存储Key的value值会被新值所替换。如果不是同一个对象,则采用链式地址法,把新的键值对对象保存到旧的键值对对象(Node<K,V>)的next变量中,形成单向链表结构,即使从链表的尾部插入。
当链表过长时,查询效率会下降,所以JDK8之后新增了红黑树作为底层数据结构,如果链表长长度超过8并且数组长度大于64时,
static final int TREEIFY_THRESHOLD = 8;
static final int MIN_TREEIFY_CAPACITY = 64;
hashMap会把这个链表转成红黑树来存储(如果数组长度没有超过64会强制扩容而不是转成红黑树,因为红黑树增删数据效率低,数组扩容后减少hash冲突几率)
当链表长度小于6时会从红黑树转回链表
static final int UNTREEIFY_THRESHOLD = 6;
jdk1.7HashMap底层采用数组+链表形式;
创建HashMap时就指定数组容量为16,加载因子为0.75,
当存放元素时,首先会根据Key的hashCode值找到数据应该保存在table数组的下标位置。
如果数组的位置上没有数据 ,直接将这这个键值对保存在该位置上。
如果数组的位置上已经有数据了,即发生了哈希冲突(哈希碰撞),也就是两个对象的key和hash值相等,那么则需要通过Key的equals()方法判断这两个对象是否为同一个对象。如果是,那么原本存储Key的value值会被新值所替换。如果不是同一个对象,也是采用链式存储,直接在数组的该位置插入Entry[]形成单向链表结构,即是从链表的头部插入。当元素个数大于数组容量*加载因子时,数组扩容为原来2倍。
3.HashSet存储原理
HashSet底层由HshMap实现,利用了HashMap的key不能重复的特性
直接将要存储的元素作为key存储到HashMap中。HashSet即是HashMap的Key。
第一次往HashSet中add()元素时,底层实际调用map的put添加元素,map的键(Key)是HashSet add()的元数,value为每次创建一个Object对象。
4.List和Set的区别
-
Set 接口实例存储不重复的数据,不强调Set的无序性,因为其间接实现类LinkedHashSet提供了通过前驱指针和后继指针实现有序遍历集合的操作。List 接口实例存储的是有序的,可以重复的元素。
-
Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
-
List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector> 。Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变<直接实现类有HashSet,间接实现类有LinkedHashSet,TreeSet>
一、存储元素的方式:
java.util.List:线性表,可以保存重复元素并且有序,可通过下标操作。
java.util.Set: 不可重复集合,这里的重复元素判定是依靠元素自身的equals方法比较的结果而定,为true就认为是重复元素则不添加。
二、添加对象:
List添加对象没有要求,Set添加对象,要求对象所在的类必须重写equals()和hasCode(),相等的对象必须具有相等的散列码。
三、检索效率
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变
List:类似动态数组,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
List接口有三个直接实现类,Set有一个直接实现类,两个间接实现类
5.HashMap和Hashtable的区别
相同点:
HashMap 和 Hashtable都实现了Map接口,Cloneable(可克隆),Serializable(可序列化)这三个接口
不同点:
1,HashMap线程不安全的,效率高;允许key和value存储null
2,Hashtable:作为古老的实现类;线程安全的,效率低;key和value不能存储null
3.Hashtable提供了对键的列举(Enumeration).
不同点:
- 底层数据结构不同:jdk1.7底层存储数据采用数组+链表,JDK1.8底层存储数据采用数组+链表+红黑树
- Hashtable 是不允许键或值为 null 的,HashMap 的键值则都可以为 null。
3.添加key-value的 hash 值算法不同:HashMap添加元素时,是使用自定义的哈希算法,而HashTable是直接采用key的hashCode()
4.实现方式不同:Hashtable 继承的是 Dictionary类,而 HashMap 继承的是 AbstractMap 类。 - 初始化容量不同:HashMap 的初始容量为:16,Hashtable 初始容量为:11,两者的加载因子默认都是:0.75。
- 扩容机制不同:当已用容量>总容量 * 加载因子时,HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 +1。
- 支持的遍历种类不同:HashMap只支持Iterator遍历,而HashTable支持Iterator和Enumeration两种方式遍历
迭代器不同:HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。而Hashtable 则不会。 - 部分API不同:HashMap不支持contains(Object value)方法,没有重写toString()方法,而HashTable支持contains(Object value)方法,而且重写了toString()方法
- 同步性不同: Hashtable是同步(synchronized)的,适用于多线程环境,而HashMap不是同步的,适用于单线程环境。多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。
由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
6.xml文件的语法要求有哪些?
1、XML 文档必须有根元素,有且只能有一个根元素,在根元素里面可以写很多子节点
2、XML 声明:XML 声明文件的可选部分,如果存在需要放在文档的第一行
3,、除了自闭合标签,所有的 XML 元素都必须有一个关闭标签
自闭合标签形式:,定义属性只能在<>里的/之前定义。
4,XML的所有标签名是自定义的,所以XML 标签对大小写敏感
5、标签成对存在,自闭合标签,可以独立存在
6.XML 中的注释采用:
7.常用的xml解析方式
XML解析常用方式:
1.DOM(Document Object Model,文档对象模型)
是官方提供的平台无关的解析方式。
会将一个XML文档内容以树的结构一次性加载到内存中,可以对XML文档的内容进行随机访问或修改的操作。但是对大文档来说解析效率低,内存成本高。
2.SAX(以事件为驱动)
以事件为驱动,需要哪些数据再加载和解析哪些内容,解析速度快,占用内存少,但是不会将内容加载到内存,使用起来不方便,反而会增加程序负担。
3.JDOM(JAVADOM用的是集合)
是java和DOM的结合体,是基于java平台使用的,简单高效,但是需要加载整个文档,对内容容量要求高。
4.DOM4J
是一个java的XML API,类似于JDOM,用来读写xml文件,性能优异,功能强大,简单易用,且开放源代码,是目前最流行、最好用的xml解析工具,解析xml速度最快
8.什么是maven
1.Maven 是 Apache 开源组织奉献的一个开源项目。Maven 这个词可以翻译为“知识的积累”,也可以翻译为“专家”或“内行”
2、Maven 的本质是一个项目管理工具,将项目开发和管理过程抽象成一个项目对象模型(POM)。开发人员只需做一些简单的配置,就可以批量完成项目的构建、报告和文档的生成工作。
3、Maven 是跨平台的,这意味着无论是在 Windows 上,还是在 Linux 或者 Mac 上,都可以使用同样的命令。
综上所诉,Maven 作为一个构建工具,不仅能帮我们自动化构建,还能够抽象构建过程,提供构建任务实现;它跨平台,对外提供了一致的操作接口,这一切足以使它成为优秀的、流行的构建工具
Maven 可以统一管理所有的依赖 jar,甚至是不同的版本。程序员也可以动态地将自己写好的模块打包成 jar 包让它管理。需要的时候,可以直接通过简单的描述文件告诉 Maven,它会自动帮助程序员找出来,集成到项目中。并且它提供了中央仓库,能帮我们自动下载构件。
总结Maven作用:
Maven 统一集中管理好所有的依赖包,不需要程序员再去寻找。
对应第三方组件用到的共同 jar包,Maven 自动解决重复和冲突问题。
Maven 作为一个开放的架构,提供了公共接口,方便同第三方插件集成。程序员可以将自己需要的插件,动态地集成到 Maven,从而扩展新的管理功能。
Maven 可以统一每个项目的构建过程,实现不同项目的兼容性管理。
扩展: jar包是将一系列的类、接口等相关文件压缩打包后形成的文件,表现为扩展名为.jar的文件。
9.什么是反射
反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
Java反射机制是一种动态机制,它允许程序运行起来后再确定实例化对象,调用方法和操作属性,可以提高代码的灵活度。
但是反射会带来较慢的运行速度和更多的系统开销,所以不能过度依赖反射机制。Java反射机制的主要功能:
1、在运行时判断任意一个对象所属的类;
2、在运行时构造任意一个类的对象;
3、在运行时调用任意一个对象的方法等。
在JDK中,主要由以下类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中:
Class类:代表一个类,位于java.lang包下。newInstance()
Field类:代表类的成员变量(成员变量也称为类的属性)。 Object get(Object obj),
需要指定对象,想要获取那个对象的属性值就哪个对象 getDeclaredField(); Field[] getFields();
获取所有公有方法 getDeclaredFields();获取所有属性包括非私有
Method类:代表类的方法。
getMethod(),
Methods[] getMehtods();
getDeclaredMethod();
getDeclaredMethods();Constructor类:代表类的构造方法。
Constructor getConstructor(Class<?>… parameterTypes);参数列表是可变参数;当参数列表为空,获取到的是无参构造
Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
10.注解的分类,你用过哪些注解,它们的作用是什么?
注解(Annocation)的概念:又叫标注,是从Java5开始增加的一种引用数据类型,是代码里的特殊标志,注解本质上就是代码中的特殊标记,通过这些标记可以在编译,类加载,以及运行时执行指定的处理。通过@interface 注解名称{ 注解成员;}定义。
注解的分类:
1.五个JDK注解:
1.@Override 限定父类重写方法
2.@Deprecated 标示已过时
3.@SuppressWarnings 抑制编译器警告
4.@SafeVarargs “堆污染”警告
5.@FunctionalInterface 函数式接口
2.元注解
用于描述注解的注解 ,通常配合元注解完成自定义注解
元注解主要有:
@Retention, 定义注解的生命周期,只能指定一个,
@Documented,
@Target, 用来指定注解可以出现的位置,比如出现在类上、方法上、属性上、参数上。 通过ElementType可以指定多个位置,在大括号中用逗号隔开
@Inherited, 指定继承关系
@Repeatable
3.自定义注解
使用@interface 注解名{
注解体;
例如:
类型 属性名() default 默认值;
}
11.谈谈你对socket的理解
Socket也叫做套接字,Java Socket是实现Java网络编程的机制,其中服务器端的套接字成为ServerSocket,而客户端的套接字称为Socket。每一个Socket都由IP地址和端口号唯一确定。
Socket可以分为两种类型:面向连接的Socket通信协议(TCP)和无连接的Socket通信协议(UDP)。
基于TCP的Socket通信过程分为三个步骤:服务器监听、客户端请求、连接确认。
Socket的生命周期可以分为三个阶段:打开Socket,使用socket收发数据和关闭socket。在Java语言中,使用socket实现客户端和服务器端的进行数据交换,与数据流的操作关系密切。Socket提供了getInputStream()和getOutputStream()来写出和读入字节数据通过网络发送给对方。
编程题
-
定义封装类Student,类中的成员有:
私有属性:学号,姓名,年龄,性别
无参构造方法,全参数构造方法
定义每个属性的setters和getters方法
重写toString方法和equals方法(根据学号比较对象)在测试类中利用反射完成以下操作:
- 用户从控制台输入要加载的类名
- 实例化该类,要求调用有参构造方法完成实例化对象的同时为属性赋值
- 获取并遍历类中的所有方法,执行所有的get开头的方法,并将其返回值打印输出。
package JAVA_API.api_homework.day18;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Scanner;
/**
* 1. 定义封装类Student,类中的成员有:
* 私有属性:学号,姓名,年龄,性别
* 无参构造方法,全参数构造方法
* 定义每个属性的setters和getters方法
* 重写toString方法和equals方法(根据学号比较对象)
* 在测试类中利用反射完成以下操作:
* 1. 用户从控制台输入要加载的类名
* 2. 实例化该类,要求调用有参构造方法完成实例化对象的同时为属性赋值
* 3. 获取并遍历类中的所有方法,执行所有的get开头的方法,并将其返回值打印输出。
* @author yyc
* 2021/9/29 19:19
*/
//在测试类中利用反射完成以下操作:
public class StudentTest {
public static void main(String[] args) throws Exception {
Student student = new Student();
System.out.println(Student.class);
// 1. 用户从控制台输入要加载的类名
Scanner scanner = new Scanner(System.in);
System.out.println("请输入类名:");
String className = scanner.nextLine();
//2.获取类对象
Class cls = Class.forName(className);
//className = cls.getSimpleName();
//3.实例化该类,要求调用有参构造方法完成实例化对象的同时为属性赋值
//创建并初始化对象的属性,需要使用有参构造器创建,第一步先获取有参构造器,需要指定形参参数类型的类对象
/* Constructor student = cls.getConstructor(int.class,String.class,int.class,char.class);
Object student1 = student.newInstance(136184119,"余易彩",18,'女');*/
//4.为了满足题意,调用构造方法实例化对象的同时为属性赋值
Object student1 = cls.getConstructor(long.class,String.class,int.class,char.class).newInstance(131141516,"yyc",18,'女');
//5.获取并遍历类中的所有方法,执行所有的get开头的方法,并将其返回值打印输出
Method[] methods = cls.getMethods();
for (Method method : methods){
//获取方法名
String methodName = method.getName();
//输出类对象中的方法
System.out.println(methodName);
//判断是否是get开头
if (methodName.startsWith("get")) {
//执行方法
System.out.println(method.invoke(student1));
}
}
}
}
2.创建Demo类,在类中定义一个私有方法 demo(),方法体中输出“私有方法”,
创建测试类Test,利用反射在运行期输入要加载的类名,然后实例化对象,然后执行该私有方法。
package JAVA_API.api_homework.day18;
import com.sun.xml.internal.bind.v2.model.annotation.RuntimeAnnotationReader;
import javax.lang.model.element.Element;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Scanner;
/**
* 2.创建Demo类,在类中定义一个私有方法 demo(),方法体中输出“私有方法”,
* 创建测试类Test,利用反射在运行期输入要加载的类名,然后实例化对象,然后执行该私有方法。
* @author yyc
* 2021/9/29 20:56
*/
public class DemoTest {
public static void main(String[] args) throws Exception {
System.out.println(Demo.class);
Scanner scanner = new Scanner(System.in);
System.out.println("请输入类名:");
String className = scanner.nextLine();
//1.获取类对象
Class cls = Class.forName(className);
//2.实例化对象
Object demo = cls.newInstance();
//创建测试类Test,利用反射在运行期输入要加载的类名,然后实例化对象,然后执行该私有方法。
Method method = cls.getDeclaredMethod("demo");
//设置强制访问
method.setAccessible(true);
//执行方法,非静态的需要传对象
method.invoke(demo);
}
}
//创建Demo类,在类中定义一个私有方法 demo(),方法体中输出“私有方法”,
class Demo {
private void demo(){
System.out.println("私有方法");
}
}