复习day1
一 . Java基础
1. JVM、JRE和JDK的区别
- JVM(Java Virtual Machine):
Java虚拟机是一个可以执行Java字节码的虚拟机进程。
Java源文件被编译成能被Java虚拟机执行的字节码文件。用于保证Java的跨平台的特性。
- JRE(Java Runtime Environment):
Java的运行环境,包括JVM+java的核心类库。
- JDK(Java Development Kit):
Java的开发工具,包括JRE+开发工具,
可以让开发者开发、编译、执行Java应用程序。
2. 环境变量path和classpath的作用是什么?
path是配置Windows可执行文件的搜索路径,即扩展名为.exe的程序文件所在的目录, 用于指定DOS窗口命令的路径。
classpath是配置class文件所在的目录,用于指定类搜索路径,JVM就是通过它来寻找该类的class类文件的。
3. GC 是什么?为什么要有GC?
GC是垃圾收集的意思,垃圾回收可以有效的防止内存泄露,有效的使用内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。
内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显式操作方法。Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉显示的垃圾回收调用。
二 .Java语法基础
1 .一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制?
答:可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。
2 .变量有什么用?为什么要定义变量?什么时候用?
变量的作用:用来存储数据。
为什么要定义变量:用来不断的存放同一类型的常量,并可以重复使用
3 .Java的数据类型有哪些?
基本数据类型(Primitive Type):byte,short,int,long,float,double,char,boolean
引用数据类型(Reference Type):类,接口,枚举,注解 ,数组
数据类型的相互转换
正向的过程: 从低字节向高字节可以自动转换 (Java中的自动类型提升 ) byte-> short-> int-> long-> float-> double
三种情况:
int -> float
long -> float
long -> double
特殊情况:char <-> int (相互转换)
4 . && 和&的区别,||和|的区别
&,|也可以在算术运算中使用;
(exp1) & (exp2) : exp1,exp2 都要执行
(exp1) | (exp2) : exp1,exp2 都要执行
(exp1) && (exp2) 若exp1 表达式返回false, exp2不执行,直接返回结果false
(exp1) || (exp2) 若exp1 表达式返回true, exp2不执行,直接返回结果true
5 .char型变量中能不能存贮一个中文汉字?为什么?
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字啦。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。补充说明:unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
6 .++i与i++有什么区别
就近原则:
i++ 先赋值后自增,例如 a=i++,先赋值a=i,后自增i=i+1,所以结果是a=1
++i 先自增再赋值,例如 a=++i,先自增i=i+1,后赋值a=i,所以结果是a=2
7 .short s1=1;s1=s1+1;有错吗?short s1=1;s1 += 1;有错吗?
对于short s1 = 1;s1 = s1 + 1;由于1是int类型,因此s1 + 1运算结果也是int类型,需要强制转换类型才能赋值给short类型,而short s1=1;s1+=1;可以正确编译,因为s1 += 1;相当于s1=(short)(s1+1);其中有隐含的强制类型转换.
8 .Java中的final关键字有哪些用法
- 修饰类: 表示该类不能被继承,即不能有子类
- 修饰方法: 表示方法不能被重写
- 修饰变量: 表示变量只能一次赋值,以后值不能被修改(常量)
9. swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
在Java 5以前,switch(expr)中,expr只能是byte、short、char、int,枚举。
从Java 7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。
10 . int与Integer的基本使用对比
(1)Integer是int的包装类;int是基本数据类型;
(2)Integer变量必须实例化后才能使用;int变量不需要;
(3)Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值 ;
(4)Integer的默认值是null;int的默认值是0
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true
Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false
-----------------------------------------------------------------------------------------------
Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false
对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false
11 .排序都有哪几种方法?请列举。口述用JAVA实现快速排序
排序的方法:插入排序、冒泡排序、快速排序、选择排序、归并排序。
使用快速排序方法对a[0:n-1]排序:
从a[0:n-1]中选择一个元素作为middle,该元素为支点
把余下的元素分割为两端left和right,使得left中的元素都小于等于支点,而right中的元素都是大于支点。
递归的使用快速排序方法对left进行排序
递归的使用快速排序方法对right进行排序。
所得结果为left+middle+right
//
- 从数组中取出一个数,作为基准数
- 分区:将比这个数[基准数]大或者等于的数全部放在它的右边,小于它的数全部放在它的左边
- 再对左右分区间重复第二步骤,直到各分区只有一个数
12 .String底层是怎么实现的
String类的一个最大特性是不可修改性,而导致其不可修改的原因是在String内部定义了一个常量数组final char数组,因此每次对字符串的操作实际上都会另外分配,分配一个新的常量数组空间
- 初始化
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
//数组定义为常量,不可修改
private final char value[];
public String() {
this.value = new char[0];
}
- 实例化字符串
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
13 .Math.floor,Math.ceil,Math.round
Math.floor 返回小于或等于一个给定数字的最大整数,向下取整
Math.ceil 返回大于或等于一个给定数字的最小整数
Math.round 返回一个数字四舍五入后最接近的整数(如果参数的小数部分恰好等于0.5,则舍入到相邻的在正无穷方向上的整数)
System.out.println("Math.floor 返回小于或等于一个给定数字的最大整数,向下取整");
System.out.println(Math.floor(11.46));//11
System.out.println(Math.floor(11.64));//11
System.out.println(Math.floor(11.5));//11
System.out.println();
System.out.println(Math.floor(-11.46));//-12
System.out.println(Math.floor(-11.64));//-12
System.out.println(Math.floor(-11.5));//-12
System.out.println("Math.floor");
System.out.println("Math.ceil 返回大于或等于一个给定数字的最小整数");
System.out.println(Math.ceil(11.46));//12
System.out.println(Math.ceil(11.64));//12
System.out.println(Math.ceil(11.5));//12
System.out.println();
System.out.println(Math.ceil(-11.46));//-11
System.out.println(Math.ceil(-11.64));//-11
System.out.println(Math.ceil(-11.5));//-11
System.out.println("Math.ceil");
System.out.println("Math.round 返回一个数字四舍五入后最接近的整数");
//如果参数的小数部分恰好等于0.5,则舍入到相邻的在正无穷方向上的整数
System.out.println(Math.round(11.46));//11
System.out.println(Math.round(11.64));//12
System.out.println(Math.round(11.5));//12
System.out.println();
System.out.println(Math.round(-11.46));//-11
System.out.println(Math.round(-11.64));//-12
System.out.println(Math.round(-11.5));//-11
System.out.println("Math.round");
14 .冒泡排序,选择排序,快速排序,直接插入排序
冒泡排序:相邻两个进行两两比较,每一轮比较完,确定一个最值(大)
public static void main(String[] args) {
int[] arr = {2, 1, 5, 8, 2, 1, 4, 6, 3};
System.out.println("冒泡排序:" + Arrays.toString(bubble(arr)));
}
//冒泡排序
public static int[] bubble(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
arr[j] = arr[j] ^ arr[j + 1];
arr[j + 1] = arr[j] ^ arr[j + 1];
arr[j] = arr[j] ^ arr[j + 1];
}
}
}
return arr;
}
选择排序:依次和后面所有的元素进行比较,确定最值(小)
//选择排序
public static int[] select(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
}
}
return arr;
}
快速排序
算法思想:分治思想:比大小,再分区
- 从数组中取出一个数,作为基准数
- 分区:将比这个数[基准数]大或者等于的数全部放在它的右边,小于它的数全部放在它的左边
- 再对左右分区间重复第二步骤,直到各分区只有一个数
实现思路:挖坑填数
- 将基准数挖出形成第一个坑
- 由后向前找比它小的数,找到后挖出此数填到前一个坑中
- 由前向后找比它大或等于的数,找到后也挖出此数填到前一个坑中
- 再重复执行2,3两步骤
//快速排序
public static void quickStart(int[] arr, int start, int end) {
if (start < end) {
//得到重合的那个位置 -分区
int index = getIndex(arr, start, end);
//左边
quickStart(arr, 0, index - 1);
//右边
quickStart(arr, index + 1, end);
}
}
private static int getIndex(int[] arr, int start, int end) {
int i = start;
int j = end;
int x = arr[i];
while (i < j) {
//由后向前找比它小的数,找到后挖出此数填到前一个坑中
while (i < j && arr[j] >= x) {
j--;
}
if (i < j) {
arr[i] = arr[j];
i++;
}
//**由前向后找比它大或等于的数,找到后也挖出此数填到前一个坑中**
while (i < j && arr[i] < x) {
i++;
}
if (i < j) {
arr[j] = arr[i];
j--;
}
}
//基准数填充到重叠处
arr[i] = x;
return i;
}
直接插入排序:将数组中剩余的值(从数组中第2个位置开始)依次直接插入到前面,保证前面的序列仍然是一个有序的序列
//直接插入排序 打牌
public static int[] directInsert(int[] arr) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0; j--) {
if (arr[j] < arr[j - 1]) {
arr[j] = arr[j] ^ arr[j - 1];
arr[j - 1] = arr[j] ^ arr[j - 1];
arr[j] = arr[j] ^ arr[j - 1];
}
}
}
return arr;
}
时间复杂度
15 .<<左移运算符,>>右移运算符,>>>无符号右移
<<:左移运算符:将一个运算对象的各二进制位全部左移若干位(左边的二进制丢弃,右边补0)
11 << 2 = 44
-14 <<2 =-56
补充:对于左移,直观的理解为,对于正数来说,左移相当于乘以2(但效率比乘法高);对于负数来说,没有直观的理解。
>>:右移运算符:将一个运算对象的各二进制位全部右移若干位,正数左补0,负数左补1
4 >> 2 = 1;
-14 >> 2 = -4;
补充:对于右移,直观的理解为,对于正数来说,右1移相当于除以2(但效率比除法高);对于负数来说,没有直观的理解。
>>> : 无符号右移,忽略符号位,空位都以0补齐
16 .斐波拉契数列,阶乘,杨辉三角(for,递归写法)
求斐波那契数列第n个位置上的值
斐波那契数列如下:1 1 2 3 5 8 13 21 34 55 …
- 递归写法
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入您要查询的第几个斐波那契数:");
int k = scanner.nextInt();
int m = fib(k);
System.out.println(m);
}
public static int fib(int n){
if (n ==1 || n == 2){
return 1;
}else {
return fib(n-1)+fib(n-2);
}
}
- for写法
public static int fei(int n){
if(n == 1 || n == 2)
return 1;
int head = 1;
int tail = 1;
int total = 0;
for(int i = 3; i <= n; i++){
total = head + tail;
tail = head;
head = total;
}
return total;
}
阶乘
- 递归写法
public static long factorial2(int n){
if (n <= 1){
return 1;
}else{
return factorial2(n-1) * n;
}
}
- for写法
public static int factorial1(int n){
int sum = 1;
for(int i = 2; i <= n; i++)
sum *= i;
return sum;
}
杨辉三角
/**
* 1
* 1 1
* 1 2 1
* 1 3 3 1
* 1 4 6 4 1
* 1 5 10 10 5 1
* @param x 纵坐标 行
* @param y 横坐标 列
*/
public static int yang(int x,int y){
if(y==0 || x==y)
return 1;
return yang(x-1,y-1) + yang(x-1,y);
}
/**
* 打印直角三角形.
* @param x 打印的杨辉三角的行数
*/
public static void printYang(int x){
for (int i = 0; i < x; i++) {
for (int j = 0; j <=i ; j++) {
System.out.print(yang(i,j)+"\t");
}
System.out.println();
}
}
递归好处:代码更简洁清晰,可读性更好
递归坏处:由于递归需要系统堆栈,所以空间消耗要比非递归代码要大很多。而且,如果递归深度太大,可能系统撑不住。
循环方法比递归方法快, 因为循环避免了一系列函数调用和返回中所涉及到的参数传递和返回值的额外开销。
17. UTF-8和GBK区别
UTF-8编码用以解决国际上字符的一种多字节编码。它对英文使用1个字节,中文3个字节。。
GBK通俗点将就是我们国家自己的编码方式。它对中英文都采用双字节表示,GBK是GB2312在量上的扩充,并没有质的区别,向后兼容。
18.java.util. concurrent 包下的API了解嘛?
java的 [java.util.concurrent]包,这是java在1.5之后提供的一个功能,用于并发编程的实用类
在Java8中,ConcurrentHashMap去除了Segment分段锁的数据结构,主要是基于CAS操作保证保证数据的获取以及使用synchronized关键字对相应数据段加锁实现了主要功能,这进一步提高了并发性,兼顾线程安全以及效率
CAS操作:会造成ABA问题;不断自询,自询时间过长