- Java基础概念
- Java和C++的区别:
- 都是面向对象的语言,都支持封装、继承和多态
- 指针:
- Java不提供指针来直接访问内存,程序更加安全
- 继承:
- Java的类是单继承的,C++支持多重继承
- Java通过一个类实现多个接口来实现C++中的多重继承
- Java中类不可以多继承,但是!!!接口可以多继承
- 内存:
- Java有自动内存管理机制,不需要程序员手动释放无用内存
- Java中值传递和引用传递:
- 值传递:
- 对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。
- 引用传递:
- 对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到所有的对象上。
- 值传递:
- 创建对象的4种方式:
- new语句创建对象
- 使用反射创建对象
- 调用对象的clone()方法创建对象
- 使用反序列化,调用java.io.ObjectInputStream对象的readObject()方法
- 内部类可以访问创建它的外部类对象的成员,包括私有成员
- JDK中常用的包:
- java.lang:唯一一个不需要导入就可以用的包,基础类
- java.io
- java.net
- java.util
- java.sql
- JDK,JRE和JVM的联系和区别:
- JDK:
- java开发工具包,是java开发环境的核心组件,并提供编译、调试和运行一个java程序所需要的所有工具,可执行文件和二进制文件,是一个平台特定的软件
- JRE:
- java运行时环境,是JVM的实施实现,提供了运行java程序的平台。JRE包含了JVM,但是不包含java编译器/调试器之类的开发工具
- JVM:
- java虚拟机,当我们运行一个程序时,JVM负责将字节码转换为特定机器代码,JVM提供了内存管理/垃圾回收和安全机制等。这种独立于硬件和操作系统,正是java程序可以一次编写多处执行的原因
- 区别:
- JDK用于开发,JRE用于运行java程序
- JDK和JRE中都包含JVM
- JVM是java编程语言的核心并且具有平台独立性。
- JDK:
- Java和C++的区别:
- 面向对象编程:
- 覆盖和重载
- 覆盖:
- 子类对父类方法的一种重写,只能比父类抛出更少的异常,访问权限不能比父类的小。
- 被覆盖的方法不能是private的,否则只是在子类中重新定义了一个方法
- 重载:
- 表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数、顺序和类型不同)
- 重载的三个条件:参数类型不同、参数个数不同、参数顺序不同
- 覆盖:
- 构造器:
- 构造函数不可以被继承,所以不可以覆盖,只可以重载
- 一个类,如果构造函数被private修饰,则为不明确的构造函数,即其不可以被子类继承(原因下边解释)
- java的初始化顺序:
- 初始化父类中的静态成员变量和静态代码块
- 初始化子类中的静态成员变量和静态代码块
- 初始化父类的普通成员变量和代码块,再执行父类的构造方法
- 初始化子类的普通成员变量和代码块,再执行子类的构造方法
- (如果父类构造函数是私有的,则不可以被执行,所以解释了为什么父类不可以被继承)
- equals和hashcode的关系:
- 当两者equals时,则hash code 值一定相同,反之不成立。
- 覆盖和重载
- 字符串:
- String s = new String("xyz");创建了几个字符串对象?
- 答:两个对象,一个是静态存储区的“xyz”,一个是用new创建在堆上的对象。
- String s = "Hello" ;s = s+"world";则原始的String对象中的内容到底变了没有?
- 没有,String是不可变类。在这段代码中,s原先指向一个String对象,内容是hello,然后我们对s进行了+操作。s指向了另外一个String对象,内容是hello world。原来的那个对象还在内存,只不过引用变量s不再指向它了。
- String s = “a”+"b"+"c"+"d";共创建了多少个对象?
- 答案:1个对象。由于编译器对字符串常量直接相加的表达式进行了优化。编译时即可去掉+号,直接将其编译成一个常量相连的结果。
- String s = new String("xyz");创建了几个字符串对象?
- Java中NIO学习笔记:
- NIO在JDK1.4中引入,提供了高速、面向块的I/O
- 流与块的比较:
- 原来的IO和NIO的最重要区别就是数据打包和传输的方式,前者以流的方式处理数据,后者以块的方式处理数据
- 面向流的IO系统一次一个字节的处理数据,面向块的IO系统每一步中产生或者消费一个数据块。
- 核心概念:
- 缓冲区(Buffer):发送给一个通道的所有对象都必须首先放在缓冲区中。
- 状态变量:
- 每一个读/写操作都会改变缓冲区的状态。通过记录和跟踪这些变化,缓冲区就可能够内部地管理自己的资源。
- position
- 缓冲区实际就是数组,position变量指定了下一个字节将放到数组的哪一个元素中
- limit
- 该变量表明还有多少数据需要取出或者还有多少空间可以放入数据
- capacity
- 该变量表明可以储存在缓冲区中的最大数据容量,即底层数组的大小。
- 访问方法:
- flip()方法:
- 当我们将缓冲中的数据输出到通道中前,要调用flip()方法,该方法的功能如下:
- 将limit设置为当前的position
- 将position设置为0
- clear方法:
- 调用缓冲区的clear()方法,以便缓冲区接收更多的字节。clear方法功能如下:
- 将limit设置为与capacity相同
- 将position设置为0
- 调用缓冲区的clear()方法,以便缓冲区接收更多的字节。clear方法功能如下:
- 当我们将缓冲中的数据输出到通道中前,要调用flip()方法,该方法的功能如下:
- flip()方法:
- 状态变量:
- 通道(Channel):从通道中读取任何数据也必须首先读取到缓冲区里。
- 选择器(Selector):
- Selector是异步I/O中的核心对象。Selector就是您注册对各种I/O事件的兴趣的地方,而且当那些事件发生时,就是这个对象告诉您所发生的事件。
- 创建选择器:Selector s = Selector.open()
- 我们将对不同的通道对象调用register()方法,以便注册我们对这些对象中发生的I/O事件的兴趣。register()的第一个参数就是这个Selector对象。
- 缓冲区(Buffer):发送给一个通道的所有对象都必须首先放在缓冲区中。
- NIO的大致流程:
- 输入文件->缓冲区->通道->缓冲区->程序处理数据->缓冲区->通道->缓冲区->输出文件
- NIO读取文件示例:
- NIO写入文件示例:
- Java基础概念
- Java和C++的区别:
- 都是面向对象的语言,都支持封装、继承和多态
- 指针:
- Java不提供指针来直接访问内存,程序更加安全
- 继承:
- Java的类是单继承的,C++支持多重继承
- Java通过一个类实现多个接口来实现C++中的多重继承
- Java中类不可以多继承,但是!!!接口可以多继承
- 内存:
- Java有自动内存管理机制,不需要程序员手动释放无用内存
- Java中值传递和引用传递:
- 值传递:
- 对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。
- 引用传递:
- 对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到所有的对象上。
- 值传递:
- 创建对象的4种方式:
- new语句创建对象
- 使用反射创建对象
- 调用对象的clone()方法创建对象
- 使用反序列化,调用java.io.ObjectInputStream对象的readObject()方法
- 内部类可以访问创建它的外部类对象的成员,包括私有成员
- JDK中常用的包:
- java.lang:唯一一个不需要导入就可以用的包,基础类
- java.io
- java.net
- java.util
- java.sql
- JDK,JRE和JVM的联系和区别:
- JDK:
- java开发工具包,是java开发环境的核心组件,并提供编译、调试和运行一个java程序所需要的所有工具,可执行文件和二进制文件,是一个平台特定的软件
- JRE:
- java运行时环境,是JVM的实施实现,提供了运行java程序的平台。JRE包含了JVM,但是不包含java编译器/调试器之类的开发工具
- JVM:
- java虚拟机,当我们运行一个程序时,JVM负责将字节码转换为特定机器代码,JVM提供了内存管理/垃圾回收和安全机制等。这种独立于硬件和操作系统,正是java程序可以一次编写多处执行的原因
- 区别:
- JDK用于开发,JRE用于运行java程序
- JDK和JRE中都包含JVM
- JVM是java编程语言的核心并且具有平台独立性。
- JDK:
- Java和C++的区别:
- 面向对象编程:
- 覆盖和重载
- 覆盖:
- 子类对父类方法的一种重写,只能比父类抛出更少的异常,访问权限不能比父类的小。
- 被覆盖的方法不能是private的,否则只是在子类中重新定义了一个方法
- 重载:
- 表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数、顺序和类型不同)
- 重载的三个条件:参数类型不同、参数个数不同、参数顺序不同
- 覆盖:
- 构造器:
- 构造函数不可以被继承,所以不可以覆盖,只可以重载
- 一个类,如果构造函数被private修饰,则为不明确的构造函数,即其不可以被子类继承(原因下边解释)
- java的初始化顺序:
- 初始化父类中的静态成员变量和静态代码块
- 初始化子类中的静态成员变量和静态代码块
- 初始化父类的普通成员变量和代码块,再执行父类的构造方法
- 初始化子类的普通成员变量和代码块,再执行子类的构造方法
- (如果父类构造函数是私有的,则不可以被执行,所以解释了为什么父类不可以被继承)
- equals和hashcode的关系:
- 当两者equals时,则hash code 值一定相同,反之不成立。
- 覆盖和重载
- 字符串:
- String s = new String("xyz");创建了几个字符串对象?
- 答:两个对象,一个是静态存储区的“xyz”,一个是用new创建在堆上的对象。
- String s = "Hello" ;s = s+"world";则原始的String对象中的内容到底变了没有?
- 没有,String是不可变类。在这段代码中,s原先指向一个String对象,内容是hello,然后我们对s进行了+操作。s指向了另外一个String对象,内容是hello world。原来的那个对象还在内存,只不过引用变量s不再指向它了。
- String s = “a”+"b"+"c"+"d";共创建了多少个对象?
- 答案:1个对象。由于编译器对字符串常量直接相加的表达式进行了优化。编译时即可去掉+号,直接将其编译成一个常量相连的结果。
- String s = new String("xyz");创建了几个字符串对象?
- Java中NIO学习笔记:
- NIO在JDK1.4中引入,提供了高速、面向块的I/O
- 流与块的比较:
- 原来的IO和NIO的最重要区别就是数据打包和传输的方式,前者以流的方式处理数据,后者以块的方式处理数据
- 面向流的IO系统一次一个字节的处理数据,面向块的IO系统每一步中产生或者消费一个数据块。
- 核心概念:
- 缓冲区(Buffer):发送给一个通道的所有对象都必须首先放在缓冲区中。
- 状态变量:
- 每一个读/写操作都会改变缓冲区的状态。通过记录和跟踪这些变化,缓冲区就可能够内部地管理自己的资源。
- position
- 缓冲区实际就是数组,position变量指定了下一个字节将放到数组的哪一个元素中
- limit
- 该变量表明还有多少数据需要取出或者还有多少空间可以放入数据
- capacity
- 该变量表明可以储存在缓冲区中的最大数据容量,即底层数组的大小。
- 访问方法:
- flip()方法:
- 当我们将缓冲中的数据输出到通道中前,要调用flip()方法,该方法的功能如下:
- 将limit设置为当前的position
- 将position设置为0
- clear方法:
- 调用缓冲区的clear()方法,以便缓冲区接收更多的字节。clear方法功能如下:
- 将limit设置为与capacity相同
- 将position设置为0
- 调用缓冲区的clear()方法,以便缓冲区接收更多的字节。clear方法功能如下:
- 当我们将缓冲中的数据输出到通道中前,要调用flip()方法,该方法的功能如下:
- flip()方法:
- 状态变量:
- 通道(Channel):从通道中读取任何数据也必须首先读取到缓冲区里。
- 选择器(Selector):
- Selector是异步I/O中的核心对象。Selector就是您注册对各种I/O事件的兴趣的地方,而且当那些事件发生时,就是这个对象告诉您所发生的事件。
- 创建选择器:Selector s = Selector.open()
- 我们将对不同的通道对象调用register()方法,以便注册我们对这些对象中发生的I/O事件的兴趣。register()的第一个参数就是这个Selector对象。
- 缓冲区(Buffer):发送给一个通道的所有对象都必须首先放在缓冲区中。
- NIO的大致流程:
- 输入文件->缓冲区->通道->缓冲区->程序处理数据->缓冲区->通道->缓冲区->输出文件
- NIO读取文件示例:
- NIO写入文件示例:
- Java集合相关知识
- HashMap和Hashtable的区别
- HashMap没有考虑同步,是线程不安全的;Hashtable使用了synchronized关键字,是线程安全的;
- 前者允许null作为Key;后者不允许null作为Key
- HashMap如何处理hash冲突?
- 拉链法
- 线性探测再散列法
- 二次探测再散列法
- 伪随机探测再散列法
- HashMap的长度为什么是2的幂次方?
- 通过将Key的hash值与length-1进行&运算,实现了当前Key的定位,2的幂次方可以减少冲突(碰撞)的次数,提高HashMap查询效率
- 如果length为2的次幂 则length-1 转化为二进制必定是11111……的形式,在于h的二进制与操作效率会非常的快,而且空间不浪费;如果length不是2的次幂,比如length为15,则length-1为14,对应的二进制为1110,在于h与操作,最后一位都为0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!这样就会造成空间的浪费。
- ConcurrentHashMap和HashTable的区别?
- ConcurrentHashMap结合了HashMap和HashTable二者的优势。HashMap没有考虑同步,hashtable考虑了同步的问题。但是hashtable在每次同步执行时都要锁住整个结构。 ConcurrentHashMap锁的方式是稍微细粒度的。 ConcurrentHashMap将hash表分为16个桶(默认值),诸如get,put,remove等常用操作只锁当前需要用到的桶。
- ConcurrentHashMap的具体实现方式:
- 该类包含两个静态内部类HashEntry和Segment;
- 前者用来封装映射表的键值对,后者用来充当锁的角色;
- Segment是一种可重入的锁ReentrantLock,每个Segment守护一个HashEntry数组里得元素,当对HashEntry数组的数据进行修改时,必须首先获得对应的Segment锁。
- 常用集合的初始容量和加载因子
- List
- ArrayList的初始容量是10;加载因子为0.5; 扩容增量:原容量的 0.5倍+1;一次扩容后长度为16
- Vector初始容量为10,加载因子是1。扩容增量:原容量的 1倍,如 Vector的容量为10,一次扩容后是容量为20
- Set
- HashSet,初始容量为16,加载因子为0.75; 扩容增量:原容量的 1 倍; 如 HashSet的容量为16,一次扩容后容量为32
- Map
- HashMap,初始容量16,加载因子为0.75; 扩容增量:原容量的 1 倍; 如 HashMap的容量为16,一次扩容后容量为32
- HashMap扩容是指链表中的元素个数超过了16*0.75=12之后开始扩容
- List
- List和Set的区别:
- List元素是有序的,可以重复
- Set元素是无序的,不可以重复
- 集合分类:
- Map接口和Collection接口是所有集合框架的父接口;
- Collection接口的子接口包括:Set接口和List接口;
- Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
- Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
- List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
- Map接口和Collection接口是所有集合框架的父接口;
- Set接口
- HashSet
- 通过元素的hashCode和equals方法保证元素的唯一性
- 添加的元素,必须实现了这两个方法,缺一不可
- TreeSet
- 让元素自身具备比较性
- 让元素实现Comparable接口,覆盖compareTo方法。
- 让集合自身具备比较性
- 自定义一个比较器类,实现Comparator接口,覆盖compare方法,建立集合对象时,将比较器传入。
- Comparable接口和Comparator接口的比较
- 前者简单,但是如果需要重新定义比较类型时,需要修改源代码。
- 后者不需要修改源代码,自定义一个比较器,实现自定义的比较方法。
- 让元素自身具备比较性
- HashSet
- Iterator和ListIterator的区别是什么?
- 前者可以遍历list和set集合;后者只能用来遍历list集合
- 前者只能前向遍历集合;后者可以前向和后向遍历集合
- 后者实现了前者,增加了一些新的功能。
- Java集合的快速失败机制“fail-fast”
- 是java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。
- 例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。
- 迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
- 解决办法:
- 在遍历过程中,所有涉及到改变modCount值得地方全部加上synchronized。
- 使用CopyOnWriteArrayList来替换ArrayList
- 通过底层拷贝数组,在拷贝的数组上进行操作
- 修改完成之后,改变原有数据
- 是java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。
- Java集合的安全失败机制“fail-safe”
- 采用安全失败机制的集合容器,在遍历时不是直接在集合内容上进行访问,而是先复制原有集合内容,在拷贝的集合上进行遍历
- java.util.concurrent包下的容器都是安全失败的,可以在多线程下并发使用,并发修改。
- HashMap和Hashtable的区别
- Java基础知识点
- JDK8有哪些新特性?
- Lambda表达式和函数式接口:
- Lambda表达式(也称为闭包),允许我们将函数当成参数传递给某个方法,或者把代码本身当做数据处理;
- 函数式接口:指的是只有一个函数的接口,java.lang.Runnable和java.util.concurrent.Callable就是函数式接口的例子;java8提供了一个特殊的注解@Functionallnterface来标明该接口是一个函数式接口
- 引入重复注解: Java 8中使用@Repeatable注解定义重复注解
- 更好的类型判断:Value.defaultValue( )
- 注解的使用场景拓宽
- 加入了新的包java.time包,包含了所有关于日期、时间、时区、持续时间和时钟操作的类
- Lambda表达式和函数式接口:
- 面向对象的含义
- 面向对象是一种思想,可以将复杂问题简单化。让我们从执行者变为了指挥者。使用封装、继承、多态来实现程序设计。
- 封装
- 继承
- 多态:指一个程序中同名的多个不同方法共存的情况
- 通过子类对父类方法的覆盖实现
- 通过同一个类中方法的重载实现(重载包括参数个数不同、参数类型不同和参数顺序不同三种情况)
- 通过将子类的对象作为父类的对象实现
- Java序列化和反序列化:
- 序列化:把对象转换为字节序列的过程
- 反序列化:把字节序列恢复为对象的过程
- 对象序列化包括如下步骤:
- 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流
- 通过对象输出流的writeObject()方法写对象
- 对象反序列化的步骤如下:
- 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流
- 通过对象输入流的readObject()方法读取对象
- Java反射机制
- 反射机制:在运行中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。即动态获取信息和动态调用对象方法的功能称为反射机制。
- 反射机制的作用
- 在运行时判断任意一个对象所属的类
- 在运行时构造一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法,生成动态代理
- 与反射相关的类;
- Class:表示类
- Field:表示成员变量
- Method:表示方法
- Constructor:表示构造器
- 抽象类和接口的区别:
- 抽象类中可以没有抽象方法;接口中的方法必须是抽象方法;
- 抽象类中可以有普通的成员变量;接口中的变量必须是static final类型的,必须被初始化,接口中只有常量,没有变量!!!
- 抽象类只能单继承,接口可以继承多个父接口;
- 特殊运算符
- ~X表示的意思是:加1取反。 ~3=-4 ~-2=1
- 移位运算符:<<和>>和>>>
- 字节流和字符流的区别:
- 字节流处理单元为1个字节,操作字节和字节数组
- 字符流处理单元为2个字节,操作字符、字符串或字符数组
- 字节流用来处理二进制数据,因为要处理很多文本数据,所以提出了字符流
- 字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的
- 字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容
- 正则表达式
- 四大方法:
- 匹配:String matches(regex)用规则来匹配整个字符串
- 切割:String split(regex)
- 替换:String replaceAll(regex,str)
- 获取:
- 将正则表达式封装成对象 Pattern p = Pattern.compile(regex)
- 让正则对象和要操作的字符串相关联 Matcher m = p.matcher(String)
- 关联后,获取正则匹配引擎 while(m.find()){System.out.println(m.group( ));}
- 通过引擎对符合规则的子串进行取出操作
-
[abc] a、b 或 c(简单类) [^abc] 任何字符,除了 a、b 或 c(否定) [a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围) [a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集) [a-z&&[def]] d、e 或 f(交集) [a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去) [a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去) -
. 任何字符(与行结束符可能匹配也可能不匹配) \d 数字:[0-9] \D 非数字: [^0-9] \s 空白字符:[ \t\n\x0B\f\r] \S 非空白字符:[^\s] \w 单词字符:[a-zA-Z_0-9] \W 非单词字符:[^\w] -
X+ X,一次或多次 X{n} X,恰好 n 次 X{n,} X,至少 n 次 X{n,m} X,至少 n 次,但是不超过 m 次
- 四大方法:
- 基本数据类型分别占多少字节
- 不使用新变量,交换两个变量的值,比如有两个变量a和b。
- 基于加减法:
- a = a+b ;
- b = a-b ;
- a = a-b ;
- 基于异或运算:
- a = a^b ;
- b = a^b ;
- a = a^b ;
- 基于加减法:
- Java类加载机制:
- 定义:
- 类加载指将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。
- 类的生命周期:
- 加载--验证--准备--解析--初始化--使用--卸载
- 加载:
- 查找并加载类的二进制数据
- 验证:
- 确保被加载的类的正确性
- 文件格式验证
- 元数据 验证
- 字节码验证
- 符号引用验证
- 确保被加载的类的正确性
- 准备:
- 为类的静态变量分配内存,并将其初始化为默认值
- 假设一个类变量的定义为public static int val = 3;,那么变量val在准备阶段过后的初始值不是3而是0。
- 解析:
- 把类中符号引用转换为直接引用
- 初始化:
- 为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。
- 类加载器:
- 启动类加载器Bootstrap ClassLoader
- 扩展类加载器ExtClassloader
- 应用类加载器AppClassLoader
- 自定义加载器
- 类加载器的职责:
- 全盘负责
- 父类委托
- 缓存机制
- 父类委托机制:(双亲委派模型)
- 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
- 意义:
- 系统类防止内存中出现多份同样的字节码
- 保证java程序安全稳定运行
- Java中类的加载有三种方法:
- 命令行启动应用时候由JVM初始化加载
- 通过Class.forName( )方法动态加载
- 通过ClassLoader.loadClass( )方法动态加载
- 定义:
- JDK8有哪些新特性?
- Java基础概念
- Java和C++的区别:
- 都是面向对象的语言,都支持封装、继承和多态
- 指针:
- Java不提供指针来直接访问内存,程序更加安全
- 继承:
- Java的类是单继承的,C++支持多重继承
- Java通过一个类实现多个接口来实现C++中的多重继承
- Java中类不可以多继承,但是!!!接口可以多继承
- 内存:
- Java有自动内存管理机制,不需要程序员手动释放无用内存
- Java中值传递和引用传递:
- 值传递:
- 对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。
- 引用传递:
- 对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到所有的对象上。
- 值传递:
- 创建对象的4种方式:
- new语句创建对象
- 使用反射创建对象
- 调用对象的clone()方法创建对象
- 使用反序列化,调用java.io.ObjectInputStream对象的readObject()方法
- 内部类可以访问创建它的外部类对象的成员,包括私有成员
- JDK中常用的包:
- java.lang:唯一一个不需要导入就可以用的包,基础类
- java.io
- java.net
- java.util
- java.sql
- JDK,JRE和JVM的联系和区别:
- JDK:
- java开发工具包,是java开发环境的核心组件,并提供编译、调试和运行一个java程序所需要的所有工具,可执行文件和二进制文件,是一个平台特定的软件
- JRE:
- java运行时环境,是JVM的实施实现,提供了运行java程序的平台。JRE包含了JVM,但是不包含java编译器/调试器之类的开发工具
- JVM:
- java虚拟机,当我们运行一个程序时,JVM负责将字节码转换为特定机器代码,JVM提供了内存管理/垃圾回收和安全机制等。这种独立于硬件和操作系统,正是java程序可以一次编写多处执行的原因
- 区别:
- JDK用于开发,JRE用于运行java程序
- JDK和JRE中都包含JVM
- JVM是java编程语言的核心并且具有平台独立性。
- JDK:
- Java和C++的区别:
- 面向对象编程:
- 覆盖和重载
- 覆盖:
- 子类对父类方法的一种重写,只能比父类抛出更少的异常,访问权限不能比父类的小。
- 被覆盖的方法不能是private的,否则只是在子类中重新定义了一个方法
- 重载:
- 表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数、顺序和类型不同)
- 重载的三个条件:参数类型不同、参数个数不同、参数顺序不同
- 覆盖:
- 构造器:
- 构造函数不可以被继承,所以不可以覆盖,只可以重载
- 一个类,如果构造函数被private修饰,则为不明确的构造函数,即其不可以被子类继承(原因下边解释)
- java的初始化顺序:
- 初始化父类中的静态成员变量和静态代码块
- 初始化子类中的静态成员变量和静态代码块
- 初始化父类的普通成员变量和代码块,再执行父类的构造方法
- 初始化子类的普通成员变量和代码块,再执行子类的构造方法
- (如果父类构造函数是私有的,则不可以被执行,所以解释了为什么父类不可以被继承)
- equals和hashcode的关系:
- 当两者equals时,则hash code 值一定相同,反之不成立。
- 覆盖和重载
- 字符串:
- String s = new String("xyz");创建了几个字符串对象?
- 答:两个对象,一个是静态存储区的“xyz”,一个是用new创建在堆上的对象。
- String s = "Hello" ;s = s+"world";则原始的String对象中的内容到底变了没有?
- 没有,String是不可变类。在这段代码中,s原先指向一个String对象,内容是hello,然后我们对s进行了+操作。s指向了另外一个String对象,内容是hello world。原来的那个对象还在内存,只不过引用变量s不再指向它了。
- String s = “a”+"b"+"c"+"d";共创建了多少个对象?
- 答案:1个对象。由于编译器对字符串常量直接相加的表达式进行了优化。编译时即可去掉+号,直接将其编译成一个常量相连的结果。
- String s = new String("xyz");创建了几个字符串对象?
- Java中NIO学习笔记:
- NIO在JDK1.4中引入,提供了高速、面向块的I/O
- 流与块的比较:
- 原来的IO和NIO的最重要区别就是数据打包和传输的方式,前者以流的方式处理数据,后者以块的方式处理数据
- 面向流的IO系统一次一个字节的处理数据,面向块的IO系统每一步中产生或者消费一个数据块。
- 核心概念:
- 缓冲区(Buffer):发送给一个通道的所有对象都必须首先放在缓冲区中。
- 状态变量:
- 每一个读/写操作都会改变缓冲区的状态。通过记录和跟踪这些变化,缓冲区就可能够内部地管理自己的资源。
- position
- 缓冲区实际就是数组,position变量指定了下一个字节将放到数组的哪一个元素中
- limit
- 该变量表明还有多少数据需要取出或者还有多少空间可以放入数据
- capacity
- 该变量表明可以储存在缓冲区中的最大数据容量,即底层数组的大小。
- 访问方法:
- flip()方法:
- 当我们将缓冲中的数据输出到通道中前,要调用flip()方法,该方法的功能如下:
- 将limit设置为当前的position
- 将position设置为0
- clear方法:
- 调用缓冲区的clear()方法,以便缓冲区接收更多的字节。clear方法功能如下:
- 将limit设置为与capacity相同
- 将position设置为0
- 调用缓冲区的clear()方法,以便缓冲区接收更多的字节。clear方法功能如下:
- 当我们将缓冲中的数据输出到通道中前,要调用flip()方法,该方法的功能如下:
- flip()方法:
- 状态变量:
- 通道(Channel):从通道中读取任何数据也必须首先读取到缓冲区里。
- 选择器(Selector):
- Selector是异步I/O中的核心对象。Selector就是您注册对各种I/O事件的兴趣的地方,而且当那些事件发生时,就是这个对象告诉您所发生的事件。
- 创建选择器:Selector s = Selector.open()
- 我们将对不同的通道对象调用register()方法,以便注册我们对这些对象中发生的I/O事件的兴趣。register()的第一个参数就是这个Selector对象。
- 缓冲区(Buffer):发送给一个通道的所有对象都必须首先放在缓冲区中。
- NIO的大致流程:
- 输入文件->缓冲区->通道->缓冲区->程序处理数据->缓冲区->通道->缓冲区->输出文件
- NIO读取文件示例:
- NIO写入文件示例: