谈一下Java的数据类型
- 基本数据类型
boolean - 布尔类型
doubler - 双精度浮点类型 float - 单精度浮点类型
byte - 字节 char - 字符
long - 长整型 short - 短整型 int - 整形 - 引用数据类型
类 接口 数组
String类能被继承吗,为什么?
不能。在Java中,只要是被定义为final的类,也可以说是被final修饰的类,就是不能被继承的
Java是单线程还是多线程?
Java 语言是多线程的
Java 语言支持多个线程的同时执行,并提供多线程之间的同步机制(关键字为 synchronized)。
首先线程必须由Tread类或者其子类来创建,创建线程的方法有两种:
1.使用类型结构为Thread(Runnable) 的构造子类将一个实现了 Runnable 接口的对象包装成一个线程。
2.从 Thread 类派生出子类并重写 run 方法,使用该子类创建的对象即为线程;
Thread 类已经实现了 Runnable 接口,因此,任何一个线程均有它的 run 方法,而 run 方法中包含了线程所要运行的代码。线程的活动由一组方法来控制。
String,Stringbuffer,StringBuilder的区别?
JVM JRE JDK 的关系
- JDK
JDK 其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe) - JRE
Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包
如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。 - JVM
Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。
什么是跨平台性?原理是什么
所谓跨平台性,是指java语言编写的程序,一次编译后,可以在多个系统平台上运行。
实现原理:Java程序是通过java虚拟机在系统平台上运行的,只要该系统可以安装相应的java虚拟机,该系统就可以运行java程序。
访问修饰符
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
public : 对所有类可见。使用对象:类、接口、变量、方法
&和&&的区别
& 两边都会执行 && 左边出现false就不会执行右边
final
用于修饰类、属性和方法;
- 被final修饰的类不可以被继承
- 被final修饰的方法不可以被重写
- 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的
final finally finalize区别
- final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表
示该变量是一个常量不能被重新赋值。 - finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块
中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。 - finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调
用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的
最后判断。
break ,continue ,return 的区别及作用
- break 结束当前的循环体
- Continue 结束正在执行的循环 进入下一个循环条件
- retern 结束当前的方法 直接返回
如何跳出当前的多重嵌套循环
在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。例如:public static void main(String[] args) { ok: for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { System.out.println("i=" + i + ",j=" + j); if (j == 5) { break ok; } } } }
重载重写的区别
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分
重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。
== 和 equals 的区别是什么
== 判断两个对象的地址值是否相等,可以理解为两个对象是否是同一个对象。
基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址
equals 判断两个对象是否相等
JDK 中常用的包有哪些
java.lang:这个是系统的基础类;
java.io:这里面是所有输入输出有关的类,比如文件操作等;
java.nio:为了完善 io 包中的功能,提高 io 包中性能而写的一个新包;
java.net:这里面是与网络有关的类;
java.util:这个是系统辅助类,特别是集合类;
java.sql:这个是数据库操作的类。
BIO,NIO,AIO 有什么区别?
简答
- BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
- NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
- AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
详细回答
- BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
- NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
- AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。
什么是反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java获取反射的三种方法
1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制
public class Student { private int id; String name; protected boolean sex; public float score; }
public class Get { //获取反射机制三种方式 public static void main(String[] args) throws ClassNotFoundException { //方式一(通过建立对象) Student stu = new Student(); Class classobj1 = stu.getClass(); System.out.println(classobj1.getName()); //方式二(所在通过路径-相对路径) Class classobj2 = Class.forName("fanshe.Student"); System.out.println(classobj2.getName()); //方式三(通过类名) Class classobj3 = Student.class; System.out.println(classobj3.getName()); } }
String
String 是最基本的数据类型吗
不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(referencetype),Java 5 以后引入的枚举类型也算是一种比较特殊的引用类型。
String有哪些特性
- 不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象
- 常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
- final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。
String为什么是不可变的吗?
简单来说就是String类利用了final修饰的char类型数组存储字符,源码如下图所以private final char value[];
String 类是 final 类,不可以被继承
String不可变但不代表引用不可以变String str = "Hello"; str = str + " World"; System.out.println("str=" + str); //输出结果 str=Hellow world;
通过反射是可以修改所谓的“不可变”对象
// 创建字符串"Hello World", 并赋给引用s String s = "Hello World"; System.out.println("s = " + s); // Hello World // 获取String类中的value字段 Field valueFieldOfString = String.class.getDeclaredField("value"); // 改变value属性的访问权限 valueFieldOfString.setAccessible(true); // 获取s对象上的value属性的值 char[] value = (char[]) valueFieldOfString.get(s); // 改变value所引用的数组中的第5个字符 value[5] = '_'; System.out.println("s = " + s); // Hello_World /* 输出结果 s = Hello World s = Hello_World*/
数组有没有 length()方法?String 有没有 length()方法
数组没有 length()方法 ,有 length 的属性。String 有 length()方法。JavaScript中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java 混淆。
String 类的常用方法都有那些?
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的
可变性
String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。
线程安全性
String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
性能
每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
多线程
线程
线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身
进程中拥有独立功能的执行单元,一般用于完成某种操作
线程的六种状态
.线程的六种状态
新建状态(New)
刚刚创建出来且未调用start的线程
可运行状态(Runnable)
只有新建状态的线程调用start方法才能进入可运行状态
受/锁阻塞状态(Blocked)
当线程运行到需要抢到锁对象代码时,但是锁对象被其他线程持有,此时线程进入锁阻塞状态
限时等待状态(Timed_waiting)
当在线程中调用Thread.sleep(毫秒值)时,线程进入休眠状态 注意:线程进入休眠状态,与是否持有锁无关, 是否持有锁对象都可以调用Thread.sleep方法 public class TestDemo { public static void main(String[] args) throws InterruptedException { Object obj = new Object(); synchronized (obj){ for (int i = 0; i < 10; i++) { Thread.sleep(500); //如果是抢到后调用sleep那么sleep期间不会释放锁(抱着锁睡!!) System.out.println(i); } } } }
无限等待状态(Waiting)
线程如何进入Waiting(无线等待状态)
a.该线程必须先持有锁对象 b.调用锁对象的wait() c.该线程会先自动释放锁对象,然后才能进入无限等待
其他线程如何唤醒Waiting状态的线程
a.其他线程首先持有锁对象(该锁对象必须是进入无限等待的那个线程释放的锁对象) b.调用锁对象的notify() c.被唤醒的线程,由于没有锁对象,先进入锁阻塞状态,只有再次持有刚刚的那个锁对象,才能进去可运行状态
消亡状态(Terminated)
线程的任务执行完毕后,线程处于退出或者消亡状态
多线程的四种创建方式
继承Thread
实现Runnable接口
通过 Callable 和 Future 创建线程
利用线程池创建线程executorService
采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
Java Thread 中 run() 与 start() 的区别
- 1.start() 方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码;通过调用 Thread 类的 start() 方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此 Thread 类调用方法 run() 来完成其运行操作的, 这里方法 run() 称为线程体,它包含了要执行的这个线程的内容, run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。
- 2.run() 方法当作普通方法的方式调用。程序还是要顺序执行,要等待 run 方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。
线程安全问题
产生原因:多个线程竞争同一资源(访问同一数据),可参考经典的生产者消费者问题。
解决方案:
run 方法内:同步代码块 synchronized {}
Public synchronized 返回值类型 方法名(){} 自动释放对象锁
使用 Lock 锁
Lock 锁需要程序员(在 finally 代码块中)手动释放。
Lock lock=new ReentranttLock() // Reentrant(可重用的)
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作,是 JDK1.5 之后出现的。
Lock 接口中的方法:
void lock() // 获取锁 void unlock() // 释放锁
Lock 接口的实现类:
java.util.concurrent.locks.ReentrantLock implements Lock
使用步骤:
- 1.在成员位置创建一个 ReentrantLock 对象。
- 2.在可能出现线程安全问题的代码前,调用 Lock 接口中的方法 lock 获取锁对象。
- 3.在可能出现线程安全问题的代码后,调用 Lock 接口中的方法 unlock 释放锁对象。
public class RunnableImpl implements Runnable{ //定义一个共享的票源 private int ticket = 100; //1.在成员位置创建一个ReentrantLock对象 Lock l = new ReentrantLock(); //设置线程任务:卖票 @Override public void run() { //使用死循环,让卖票重复的执行 while(true){ //2.在可能出现线程安全问题的代码前,调用Lock接口中的方法lock获取锁对象 l.lock(); //判断票是否大于0 if(ticket>0){ //为了提高线程安全问题出现的几率,让程序睡眠10毫秒 try { //可能会产生异常的代码 Thread.sleep(10); //进行卖票 ticket-- System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!"); ticket--; } catch (InterruptedException e) { //异常的处理逻辑 e.printStackTrace(); }finally { //一定会执行的代码,一般用于资源释放(资源回收) //3.在可能出现线程安全问题的代码后,调用Lock接口中的方法unlock释放锁对象 l.unlock();//无论程序是否有异常,都让锁对象释放掉,节约内存,提高程序的效率 } } } } }
集合和数组的区别
数组是固定长度的;集合可变长度的。
数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。
常用的集合类
Map接口和Collection接口是所有集合框架的父接口
1.Collection接口的子接口包括:Set接口和List接口
2.Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
3.Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
4.List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
常见的数据结构
a.栈结构: 先进后出
b.队列结构: 先进先出
c.数组结构: 查询快,增删慢
d.链表结构: 查询慢,增删快
e.红黑树结构(了解)
红黑树的特点: 查询速度非常恐怖
树结构,二叉树,二叉查找树,平衡二叉树,....,黑红树
Collection
增: public boolean add(E e); 向集合中添加元素,返回值表示是否添加成功 删: public boolean remove(Object e); 从集合中删除某个元素,返回值表示是否删除成功 改: 无! 查: 无! 其他: public int size(); 获取元素个数 public boolean isEmpty(); 判断集合是否为空集合(size==0?) public void clear(); 清空集合(把集合的所有元素都删除) public boolean contains(Object e); 判断集合中是否包含某元素 public Object[] toArray(); 将集合转成数组 public Iterator iterator(); 获取当前集合的迭代器对象
List
a.有序的:在Java中元素存入和取出的顺序一致时,称为有序(比如:2 1 3 打印集合2 1 3) b.有索引 c.元素是可重复 List接口(继承Collection,肯定拥有Collection中的7+1) 特有方法: 4个,增删改查一样一个,并且都和索引相关 add(int index,E e); 在指定位置添加元素 remove(int index); 删除指定位置的元素 set(int index,E newE);将指定位置的元素改为新的元素 get(int index);根据索引获取元素 常见实现类: ArrayList<E> 底层就是采用数组结构,特点查询快,增删慢! LinkedList<E> 底层采用链表结构,特点就是查询慢,增删快!! 底层使用双向链表 Vector<E>
Set
a.无序(在Java中无序是指存入和打印时的顺序不一定一致,比如存入1 2 3 取出 1 3 2) b.无索引 c.元素唯一 注意事项: 做题的时候,列出Set接口的两个特点: b和c 在Set接口中有一个实现类是有序的(LinkedHashSet有序的) Set接口的方法(继承Collection,那么就拥有7+1方法) 特有方法: 无!! 实现类: HashSet 没有特有方法 底层采用的是哈希表结构=数组结构+链表结构+红黑树结构(JDK1.8中加入的) 特点:无序,无索引,元素唯一 LinkedHashSet 没有特有方法 底层采用的是链表+哈希表(链式哈希表) 特点: 有序的,无索引,元素唯一 TreeSet 没有特有方法 底层采用的是红黑树结构 特点: 无序(但是它具有自然顺序)!!,无索引,元素唯一 注意: TreeSet是无序的,但是它是无序中一个比较特殊的存在 (存入132打印123,存入312打印123,存入321打印123,存入123打印123)
常用集合
Map
什么是Map集合: Map集合是一种双列集合!! Map集合的特点: a.Collection的元素是单独存在的,Map的元素是成对存在的 b.Collection称为单列集合,Map称为双列集合 c.Map集合中键是唯一的,值是可以重复的,通过键最多只能找到与之对应的一个值 a.Map<K,V>集合是双列集合的根接口,具有两个泛型 其中K代表键的类型,V代表值的类,K的类和V的类型可以随意,可一样可不一样 b.Map接口有3个实现类: HashMap: 底层采用哈希表结构,无序的 LinkedHashMap:底层采用链式哈希表,有序的 TreeMap:底层采用红黑树结构, 无序的(会按照键的自然顺序默认升序) 共同的特点: Map集合的所有实现类键都是唯一的,值是可以重复的 如果键是自定义类型,为了保证键的唯一性,我们需要重写hashCode和equals方法
Map集合的遍历方式
1.根据键找值
2.获取所有的键值对对象集合,通过迭代器 / 增强for遍历
3.通过对象中的所有values方法来找到值。
public class map集合遍历{ public Static void main(String agrs){ Set<String> keys = map.keySet(); for(String key : keys ){ String values = key.getValues() } Set<Map.Entry<String,String>> keyentry = map.keyEntry(); Iterator<Map.Entry<String,String>> iterator = keyentry.iterator(); while(iterator.hasNext()){ Map.Entry<String,String> entry = iterator.next(); String key = entry.getKey(); String value =entry.getValues; } for(Map.Entry<String,String entry : iterator){ String key = entry.getKey(); String value =entry.getValues; } Collection<String> values = map.values; for(String value : values){ System.out.println(values); } } }