目录
java 中的 Math.round(-1.5) 等于多少?
equals与==的区别(Java里面字符串的比较要用到equals来比较是否相同)
是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?
类和对象java是一个面向对象的过程,只关注对象之间的联系,不关注过程
protected:同一个包同一个类,同一个包不同类,不同包子类(继承关系)
作用:保护子类,子类可以使用它修饰成员,其他的不可以,相当于传递给子类的一种继承东西
private:只能在当前类里面访问和修改,如果需要访问,需要提供公开接口(get 和set)
default:默认权限,包访问权限 (同一个包不同类与同一个包同一个类)
String和StringBuilder、StringBuffer的区别?
-
Java底层
- JDK是Java开发工具包,它不仅提供了Java程序员运行所需的JRE,还提供一系列的编译,运行工具如javac,java等。
- JRE是java程序的运行环境,包含JVM和Java基础类库。
- JVM是java虚拟机,运行java代码
JDK1.8之后有哪些新特性?
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可
Lambda 表达式和函数式接口:Lambda 表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中),使用 Lambda 表达式使代码更加简洁,但是也不要滥用,否则会有可读性等问题.
Stream API:用函数式编程方式在集合类上进行复杂操作的工具,配合Lambda表达式可以方便的对集合进行处理。Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
日期时间API:Java 8 引入了新的日期时间API改进了日期时间的管理。
-
Java的基本数据类型以及他们的包装类
基本数据类型:数据直接存储在栈上
面试题: public static void main(String[] args) { Integer a = 128, b = 128, c = 127, d = 127; System.out.println(a == b); false System.out.println(c == d); true } 执行 Integer a = 128,相当于执行:Integer a = Integer.valueOf(128),基本类型自动转换为包装类的过程称为自动装箱 在 Integer 中引入了 IntegerCache 来缓存一定范围的值,IntegerCache 默认情况下范围为:-128~127 面试题:byte类型127+1等于多少? 加起来二进制为10000000,最高位是1,是负数,①对各位取反得01111111,转换为十进制就是127,加上负号得-127,再减去1得-128;
字符串类型 (引用数据类型)
引用类型都是由类编辑器定义的,用于访问对象,这些变量被称为不可变。
引用数据类型区别:数据存储在堆上,栈上只存储引用地址
-
运算符
基本运算 + - * /
1.java%左右两边可以是小数,也可以是负数 2.Java中a/0是不可以的,会报错 3.两侧操作数类型不一致,会进行类型提升,向类型大提升 面试题: // 代码块1 short s1 = 1; s1 = s1 + 1; 编译报错,小类型不能向大类型提升 // 代码块2 short s1 = 1; s1 += 1; 通过,复合运算可以去自动进行类型提升
逻辑运算 && ||
表达式1 && 表达式2(两个表达式必须是布尔表达式,第一个表达式为false,不会执行第二个) 表达式1 || 表达式2(两个表达式必须是布尔表达式,第一个表达式为true,不会执行第二个) 面试题:&和&&的区别? &&:逻辑与运算符。当运算符左右两边的表达式都为 true,才返回 true。同时具有短路性,如果第一个表达式为 false,则直接返回 false。 &:逻辑与运算符、按位与运算符。 按位与运算符:用于二进制的计算,只有对应的两个二进位均为1时,结果位才为1 ,否则为0。 逻辑与运算符:& 在用于逻辑与时,和 && 的区别是不具有短路性。所在通常使用逻辑与运算符都会使用 &&,而 & 更多的适用于位运算。
位运算符 & | ^
& :按位与运算符,对应位有0则位0 | :按位或运算符,对应位有1就是1 ^ :按位异或,相同为0,相异为1
移位运算 << >> >>>
>>: 符号右移,减少2倍 <<: 左移,左边丢弃,右边补0 >>>:无符号右移,符号位直接补0 面试题:用最有效率的方法计算2乘以8? 2>>3
java 中的 Math.round(-1.5) 等于多少?
Math提供了三个与取整有关的方法:ceil(向上取整)、floor(向下取整)、round(四舍五入)
-
逻辑结构
顺序结构
选择结构
面试题:不能做switch参数的数据类型 ? 在java中不能做switch参数数据类型:long,float,double,boolean java5开始支持enum(枚举)
循环结构
-
equals与==的区别(Java里面字符串的比较要用到equals来比较是否相同)
对于基本类型,==比较的是值;
对于引用类型,==比较的是地址;
equals不能用于基本类型的比较;
如果没有重写equals,equals就相当于==;
如果重写了equals方法,equals比较的是对象的内容
//equal与==的区分
public static void main(String[] args) {
String a=new String("ab");
String b=new String("ab");
System.out.println(a==b); //false
System.out.println(a.equals(b)); //true
}
-
方法
方法的实参与形参关系
形参是实参的临时拷贝,形参的改变不会影响实参 java在堆中,不能从栈上获取地址,如果要改变两个变量的内容,需要把他们放在堆上
方法的重载和重写区别
面试题:方法重载和重写的区别? 方法的重载: 方法名称一致 ,方法参数(数据类型,个数和顺序不一致),返回值没有要求 好处:不需要记过多的方法名,降低成本 方法的重写: 方法名称一致,方法参数(数据类型,个数和顺序一致),返回值一致
在 Java 中,什么时候用重载,什么时候用重写?
1)重载是多态的集中体现,在类中,要以统一的方式处理不同类型数据的时候,可以用重载。 2)重写的使用是建立在继承关系上的,子类在继承父类的基础上,增加新的功能,可以用重写。
递归
定义:方法在执行的过程中调用自身 当没有结束条件的时候,会出现栈溢出的情况 步骤:1.递的时候只执行一部分,就去执行另一部分 2.归的时候,会把当前剩余部分执行完毕 3.递与归次数是一致的 4.递开辟栈空间因方法没有执行完会放在那,继续执行另一部分,遇到return才会回收
是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?
区分两种情况,发出调用时是否显示创建了对象实例。
1)没有显示创建对象实例:不可以发起调用,非静态方法只能被对象所调用,静态方法可以通过对象调用,也可以通过类名调用,所以静态方法被调用时,可能还没有创建任何实例对象。因此通过静态方法内部发出对非静态方法的调用,此时可能无法知道非静态方法属于哪个对象。
public class Demo { public static void staticMethod() { // 直接调用非静态方法:编译报错 instanceMethod(); } public void instanceMethod() { System.out.println("非静态方法"); } }
2)显示创建对象实例:可以发起调用,在静态方法中显示的创建对象实例,则可以正常的调用。
public class Demo { public static void staticMethod() { // 先创建实例对象,再调用非静态方法:成功执行 Demo demo = new Demo(); demo.instanceMethod(); } public void instanceMethod() { System.out.println("非静态方法"); } }
-
数组
定义
相同类型元素的集合在内存中是一段连续访问的空间
初始化数组
直接赋值: int [ ] 数组名={1,2,3,4,5}; 开辟新内存(动态初始化): int [ ] arr=new int[] []{1,2,3,4,5} 只分配内存,没有进行数据赋值 int [] arr=new int[]
遍历数组
方法1: public static void main(String[] args) { int []array={1,2,3,4,5,6,7}; System.out.println(Arrays.toString(array)); } 方法2: public static void main2(String[] args) { int []array={1,2,3,4,5,6,7}; for (int x:array) { 数组每个元素:数组 System.out.print(x+" "); } } 方法3: public static void main1(String[] args) { int []array={1,2,3,4,5,6,7}; for (int i = 0; i < array.length; i++) { System.out.print(array[i]); } System.out.println(); }
数组是引用类型
引用变量并不直接存储对象本身,存储的是对象在堆中空间的起始地址,通过该地址去引用变量操作对象 一个引用不能同时指向多个对象 int [ ] array=null ,代表这个引用不指向任何一个对象,不能对他进行读和写操作,否则会发生空指针异常
数组的应用
数组拷贝 法1: public static void main(String[] args) { int [] arr1={1,2,3,4}; int [] arr2=Arrays.copyOf(arr1,arr1.length); System.out.println(Arrays.toString(arr2)); } 法2: public static void main6(String[] args) { int [] arr1={1,2,3,4}; int [] arr2=new int[arr1.length]; for (int i = 0; i < arr1.length; i++) { arr2[i]=arr1[i]; } System.out.println(Arrays.toString(arr2)); } 数组扩容 扩容2倍: public static void main(String[] args) { int [] arr1={1,2,3,4}; int [] arr2=arr1.clone(); System.out.println(Arrays.toString(arr2)); } 整体拷贝: public static void main8(String[] args) { int [] arr1={1,2,3,4}; int [] aopy=Arrays.copyOf(arr1,2*arr1.length); System.out.println(Arrays.toString(aopy)); } //冒泡排序 public static int[] bubblesort(int[] array){ //比较趟数 for (int i = 0; i < array.length-1; i++) { //每趟比较的次数 boolean flag=false; //进行优化,不能放在for循环外面,否则会一直为true,它只在开始的时候判断一次,一次为true,一直就是true for (int j = 0; j < array.length-1-i; j++) { if (array[j]>array[j+1]){ int tmp=array[j]; array[j]=array[j+1]; array[j+1]=tmp; } } if (flag==false){ return array; } } return array; } public static void main(String[] args) { int [] array={3,7,8,5,10}; System.out.println(Arrays.toString(bubblesort(array))); } 二分查找 public static int binarysearch(int[]array,int k){ int left=0; int right=array.length-1; while (left<=right){ int mid=(left+right)/2; if (array[mid]<k){ left=mid+1; }else if (array[mid]>k){ right=mid-1; }else { return mid; } } return -1; } public static void main11(String[] args) { int[]array={1,2,3,4,5,6,7,8}; int k=7; System.out.println(binarysearch(array,k)); }
二维数组定义
二维数组是特殊的一维数组,每一行是一个一维数组,行数:一维数组长度 列数:一维数组中一个元素长度
初始化二维数组(不规则二维数组可以省略列,但是不能省略行)
int[][]arr1=new int[2][3]; int[][]arr2=new int[][]{{1,2,3},{4,5,6}}; int[][]arr3={{1,2,3},{4,5,6}};
遍历二维数组
法1: Arrays.deepToString public static void main(String[] args) { int[][]arr3={{1,2,3},{4,5,6}}; System.out.println(Arrays.deepToString(arr3)); } 法2: public static void main16(String[] args) { int[][]arr3={{1,2,3},{4,5,6}}; for (int[] arr:arr3) { for (int x: arr) { System.out.print(x+" "); } System.out.println(); } } 法3: public static void main15(String[] args) { int[][]arr3={{1,2,3},{4,5,6}}; for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { System.out.print(arr3[i][j]+" "); } System.out.println(); } }
-
类和对象java是一个面向对象的过程,只关注对象之间的联系,不关注过程
1.一般一个文件当中只定义一个类
2. main方法所在的类一般要使用public修饰(注意:Eclipse默认会在public修饰的类中找main方法)
3. public修饰的类必须要和文件名相同
4. 不要轻易去修改public修饰的类的名称
类和对象定义
类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量 eg:类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东 西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空 间
this的引用
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该 引用去访问
初始化对象
public static void main(String[] args) { int a; System.out.println(a); } // Error:(26, 28) java: 可能尚未初始化变量a 方法内部局部变量不初始化对象会产生编译错误情况
构造方法
实例化对象一定会调用构造方法(没有编译器也会自动化提供) 当我们没有提供任何构造方法的时候,编译器会默认给我们提供系一列的不带参数的构造方法,当构造方法调用完成后,对象才产生。 一旦提供一种方法,编译器不会默认提供一个不带参数的构造方法 class Student{ public int age; public String name; public Student(){ this("xiao",11); System.out.println("带两个参数"); } } public Student(String name,int age){ System.out.println("带两个参数"); this.name=name; this.age=age; }
注意:如果调用本类其他不带参数构造方法,必须要放在构造方法里面,必须是第一行,并且不能形成死循环
-
访问修饰限定符
- 包:多个类收集成一个组,这个组就是一个包
public:可以被所有的其他类进行访问
protected:同一个包同一个类,同一个包不同类,不同包子类(继承关系)
作用:保护子类,子类可以使用它修饰成员,其他的不可以,相当于传递给子类的一种继承东西
private:只能在当前类里面访问和修改,如果需要访问,需要提供公开接口(get 和set)
default:默认权限,包访问权限 (同一个包不同类与同一个包同一个类)
-
类的三大特性
封装
定义:对类内部实现细节的封装(隐藏),对外提供一些公开的接口供其他用户访问 作用:对外隐藏內部实现细节,增强程序安全性
继承
定义:对共性的抽取从而达到代码复用
语法: 修饰符 class 子类 extends 父类 { // ... } 一般情况下继承不超过3层,私有成员可以被继承但是不能访问
父类成员访问: 如果访问的成员变量子类中有,优先访问自己的成员变量。 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。 如果访问的成员变量与父类中成员变量同名,则优先访问自己的 1.子类访问父类的成员变量(子类不存在与父类同名的情况) public class Base { int a; int b; } public class Derived extends Base{ int c; public void method(){ a = 10; // 访问从父类中继承下来的a b = 20; // 访问从父类中继承下来的b c = 30; // 访问子类自己的c } } 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到 则访问,否则编译报错。 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错 2.子类访问父类的成员方法(不同名) public class Base { public void methodA(){ System.out.println("Base中的methodA()"); } } public class Derived extends Base{ public void methodB(){ System.out.println("Derived中的methodB()方法"); } public void methodC(){ methodB(); // 访问子类自己的methodB() methodA(); // 访问父类继承的methodA() // methodD(); // 编译失败,在整个继承体系中没有发现方法methodD() } }
super关键字
super:只会访问从父类继承的,为了程序的可读性,而不是为了父类的引用 1. 只能在非静态方法中使用 2. 在子类方法中,访问父类的成员变量和方法。 // 访问父类的成员变量时,需要借助super关键字 // super是获取到子类对象中从基类继承下来的部分 super.a = 200; super.b = 201
面试题:super和this的区别? 相同:1.都是java关键字 2.都只能在类的非静态方法中使用 3.都放在构造方法第一句,且不能同时存在 不同:1.this是对当前对象进行引用,super只访问从父类继承的 2.this用来访问本类的方法和属性 ,super访问从父类继承的方法和属性 3.super()调用父类构造方法 与this()访问构造方法 不能同时存在 4.编辑器一定会有一个super调用,但是this调用用户不写则没有
多态
定义:去完成某个行为,当不同对象去完成会产生不同的状态,对象不一样,行为就可能不一样 条件:继承,向上转型,重写 重写: 1.方法名相同,返回值相同,参数列表相同(数据类型,个数,顺序) 例如:Animal: public void eat(){ System.out.println("吃饭") } Dog:public void eat(){ System.out.println("吃饭") } 特点:1。子类访问权限》=父类的访问权限 2.private,final,static修饰方法不能被重写 3.返回值可以不一样,但是必须得构成父子关系才可以 4.构造方法也不能被重写,构造方法是重载 向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。 语法格式:父类类型 对象名 = new 子类类型() public class TestAnimal { // 2. 方法传参:形参为父类型引用,可以接收任意子类的对象 public static void eatFood(Animal a){ a.eat(); } // 3. 作返回值:返回任意子类对象 public static Animal buyAnimal(String var){ if("狗" == var){ return new Dog("狗狗",1); }else if("猫" == var){ return new Cat("猫猫", 1); }else{ return null; } } public static void main(String[] args) { Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象 Dog dog = new Dog("小七", 1); eatFood(cat); eatFood(dog); Animal animal = buyAnimal("狗"); animal.eat(); animal = buyAnimal("猫"); animal.eat(); } } 一个引用调用同一个方法,引用对象不一致,所表现的行为也不一致(多态) 向下转型:极度不安全,且必须使用instance来判断是不是这个实例
面试题: 面向对象的三个基本特征? 面向对象的三个基本特征是:封装、继承和多态。 继承:让某个类型的对象获得另一个类型的对象的属性的方法。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。 封装:隐藏部分对象的属性和实现细节,对数据的访问只能通过外公开的接口。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。 多态:对于同一个行为,不同的子类对象具有不同的表现形式。多态存在的3个条件:1)继承;2)重写;3)父类引用指向子类对象。
-
Static用法
static修饰成员变量
public class Student{ public String name; public String gender; public static String classrom="111"; } static修饰成员变量,不属于某个类的属性,是所有对象共享的 通过类名.class去访问 变量存储在方法区里面,不在堆区 生命周期伴随类一生
static修饰成员方法
public class Student{ // ... private static String classRoom = "Bit306"; // ... public static String getClassRoom(){ return classRoom; } } 1. 不属于某个具体的对象,是类方法 2. 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用 3. 不能在静态方法中访问任何非静态成员变量
Java 静态变量和成员变量的区别
- 成员变量存在于堆内存中。静态变量存在于方法区中。
- 成员变量与对象共存亡,随着对象创建而存在,随着对象被回收而释放。静态变量与类共存亡,随着类的加载而存在,随着类的消失而消失。
- 成员变量所属于对象,所以也称为实例变量。静态变量所属于类,所以也称为类变量。
- 成员变量只能被对象所调用 。静态变量可以被对象调用,也可以被类名调用。
在 Java 中,为什么不允许从静态方法中访问非静态变量?
静态变量属于类本身,在类加载的时候就会分配内存,可以通过类名直接访问; 非静态变量属于类的对象,只有在类的对象产生时,才会分配内存,通过类的实例去访问; 静态方法也属于类本身,但是此时没有类的实例,内存中没有非静态变量,所以无法调用。
-
代码块
静态代码块
// 静态代码块 static { classRoom = "bit306"; System.out.println("I am static init()!"); } 静态代码块不管生成多少个对象,其只会执行一次,类加载的时候就会被执行 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
实例代码块
public class Student{ //实例成员变量 private String name; private String gender; private int age; private double score; public Student() { System.out.println("I am Student init()!"); } //实例代码块 { this.name = "bit"; this.age = 12; this.sex = "man"; System.out.println("I am instance init()!"); } 构造块:定义在类中的代码块(不加修饰符),也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
局部代码块
访问对象中的成员
普通成员变量:通过对象引用和点号来进行询问 静态成员变量:类名和点号来进行访问
执行顺序,先静态,在实例,在不带参数的构造方法
-
抽象类(abstract)
定义
一个类中没有包含足够的信息去描述一个具体对象,这个类就是抽象类
特点
1.抽象类使用abstract来修饰 2.抽象类当中可以包含普通类所包含的普通成员 3.抽象类和普通类不一样的是可以包含抽象方法 4.抽象方法使用abstract修饰,这个方法无具体实现 5.抽象类不能实例化 6.抽象类存在的最大意义就是为了被继承 7.如果一个普通类继承了抽象类,必须重写方法 8.如果一个抽象类A继承了抽象类B,A不需要重写B的方法,但是如果A被普通类继承,就需要重写方法 9.抽象方法不能是私有的 10.final和static不可以修饰抽象方法,他们与abstract是矛盾的 11.抽象类可以有很多构造方法,方便子类进行调用,来初始化1抽象类当中的成员 // 抽象类:被abstract修饰的类 public abstract class Shape { // 抽象方法:被abstract修饰的方法,没有方法体 abstract public void draw(); abstract void calcArea(); // 抽象类也是类,也可以增加普通方法和属性 public double getArea(){ return area; } protected double area; // 面积 } 作用:使用抽象类相当于多了一重编译器的校验.实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题
普通类和抽象类区别?
抽象类不能被实例化; 抽象类可以有抽象方法,只需申明,无须实现; 有抽象方法的类一定是抽象类; 抽象类的子类必须实现抽象类中的所有抽象方法,否则子类仍然是抽象类; 抽象方法不能声明为静态、不能被static、final修饰。
-
接口
定义
多个类公共规范,是一种引用数据类型
特点
1.使用interface来修饰接口 public interface 接口名称{ // 抽象方法 public abstract void method1(); // public abstract 是固定搭配,可以不写 public void method2(); abstract void method3(); void method4(); // 注意:在接口中上述写法都是抽象方法,跟推荐方式4,代码更简洁 } 2.接口当中成员方法不可以有具体的实现(抽象方法默认是public abstract, JDK1.8开始,允许可以实现方法,但必须由default修饰) public interface USB { void openDevice(); // 编译失败:因为接口中的方式默认为抽象方法 // Error:(5, 23) java: 接口抽象方法不能带有主体 void closeDevice(){ System.out.println("关闭USB设备"); } } 3.可以有1个静态方法,所有方法默认是public 4.成员变量默认是public static final 5.接口不能被实例化,不能new 6.类和接口之间用implements来实现多个接口 7.子类重写抽象方法必须加上public 8.接口中不能有静态方法或者静态代码块 9.如果不想实现接口方法,把这个类定义成抽象类,若是这个类被其他类所继承,则需要重写 10.一个类可以实现多个接口,使用implements用逗号隔开,可以解决多继承问题
面试题:接口和抽象类区别?
(1)接口
接口使用interface修饰;
接口不能实例化;
类可以实现多个接口;①java8之前,接口中的方法都是抽象方法,省略了public abstract。②java8之后;接口中可以定义静态方法,静态方法必须有方法体,普通方法没有方法体,需要被实现;
(2)抽象类
抽象类使用abstract修饰;
抽象类不能被实例化;
抽象类只能单继承;
抽象类中可以包含抽象方法和非抽象方法,非抽象方法需要有方法体;
如果一个类继承了抽象类,①如果实现了所有的抽象方法,子类可以不是抽象类;②如果没有实现所有的抽象方法,子类仍然是抽象类。
1.comparable只能比较一个
//给学生进行排序//自定义类型如果需要比较,则需要用Comparable接口 class Student implements Comparable<Student>{ public String name; public int age; //构造方法 public Student(String name, int age) { this.name = name; this.age = age; } @Override public int compareTo(Student o) { if (this.age-o.age>0){ return 1; }else if (this.age-o.age<0){ return -1; }else { return 0; } } } public class Text1 { //给一个自定义数组排序 public static void main(String[] args) { Student student1=new Student("笑话",10); Student student2=new Student("小小",10); System.out.println(student1.compareTo(student2)); } }
2.comparator接口不止一个方法,可以比较多个
class Student { public String name; public int age; public Student(String name, int age) { this.name = name; this.age = age; } } class NameComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return o1.name.compareTo(o2.name); } } class AgeComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return o1.age-o2.age; } } public class Text1 { //给一个自定义数组排序 public static void main(String[] args) { Student[] student=new Student[3]; student[0]=new Student("bit",10); student[1]=new Student("hello",20); NameComparator nameComparator=new NameComparator(); Arrays.sort(nameComparator.compare(student)); } }
-
深拷贝和浅拷贝区别?
数据分为基本数据类型和引用数据类型。基本数据类型:数据直接存储在栈中;引用数据类型:存储在栈中的是对象的引用地址,真实的对象数据存放在堆内存里。
浅拷贝:对于基础数据类型:直接复制数据值;对于引用数据类型:只是复制了对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值随之改变。
深拷贝:对于基础数据类型:直接复制数据值;对于引用数据类型:开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象。
深拷贝相比于浅拷贝速度较慢并且花销较大。
-
String类
给字符串赋值
1.String str="hello" 直接赋值 2.String str=new String("abc"); 3.char [] cgars={'1','2','3'} 字符数组 String str=new String(cgars); 字符数组转化成字符串
String类常用方法(面试题)
(1)常见String类的获取功能 length:获取字符串长度; charAt(int index):获取指定索引位置的字符; indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引; substring(int start):从指定位置开始截取字符串,默认到末尾; substring(int start,int end):从指定位置开始到指定位置结束截取字符串; (2)常见String类的判断功能 equals(Object obj): 比较字符串的内容是否相同,区分大小写; contains(String str): 判断字符串中是否包含传递进来的字符串; startsWith(String str): 判断字符串是否以传递进来的字符串开头; endsWith(String str): 判断字符串是否以传递进来的字符串结尾; isEmpty(): 判断字符串的内容是否为空串""; (3)常见String类的转换功能 byte[] getBytes(): 把字符串转换为字节数组; char[] toCharArray(): 把字符串转换为字符数组; String valueOf(char[] chs): 把字符数组转成字符串。valueOf可以将任意类型转为字符串; toLowerCase(): 把字符串转成小写; toUpperCase(): 把字符串转成大写; concat(String str): 把字符串拼接; (4)常见String类的其他常用功能 replace(char old,char new) 将指定字符进行互换 replace(String old,String new) 将指定字符串进行互换 trim() 去除两端空格 int compareTo(String str) 会对照ASCII 码表 从第一个字母进行减法运算 返回的就是这个减法的结果,如果前面几个字母一样会根据两个字符串的长度进行减法运算返回的就是这个减法的结果,如果连个字符串一摸一样 返回的就是0。
如何将字符串反转?
添加到StringBuilder中,然后调用reverse()。
字符串常量池(底层是一个String table的哈希表)
面试题:以下代码创建了几种实例化对象?
String str="hello" 创建了1个对象
String str=new String("hello")创建了2个对象
String str=new String(new char[]{'h','e','l','l','o'} 创建3个对象
面试题1:String str="i"与 String str=new String(“i”)一样吗? String str="i"会将起分配到常量池中,常量池中没有重复的元素,如果常量池中存中i,就将i的地址赋给变量,如果没有就创建一个再赋给变量。 String str=new String(“i”)会将对象分配到堆中,即使内存一样,还是会重新创建一个新的对象。 面试题2:new String("a") + new String("b") 会创建几个对象? 对象1:new StringBuilder() 对象2:new String("a") 对象3:常量池中的"a" 对象4:new String("b") 对象5:常量池中的"b" 深入剖析:StringBuilder中的toString(): 对象6:new String("ab") 强调一下,toString()的调用,在字符串常量池中,没有生成"ab" 面试题3: String s1 = new String("1") + new String("1");//s1变量记录的地址为:new String s1.intern();//在字符串常量池中生成"11"。如何理解:jdk6:创建了一个新的对象"11",也就有新的地址;jdk7:此时常量池中并没有创建"11",而是创建了一个指向堆空间中new String("11")的地址; String s2 = "11"; System.out.println(s1 == s2);//jdk6:false;jdk7:true 面试题4:String s = new String("xyz") 创建了几个字符串对象? 一个或两个。如果字符串常量池已经有“xyz”,则是一个;否则,两个。 当字符创常量池没有 “xyz”,此时会创建如下两个对象: 一个是字符串字面量 "xyz" 所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,此时该实例也是在堆中,字符串常量池只放引用。 另一个是通过 new String() 创建并初始化的,内容与"xyz"相同的实例,也是在堆中。 面试题5:String s = "xyz" 和 String s = new String("xyz") 区别? 两个语句都会先去字符串常量池中检查是否已经存在 “xyz”,如果有则直接使用,如果没有则会在常量池中创建 “xyz” 对象。 另外,String s = new String("xyz") 还会通过 new String() 在堆里创建一个内容与 "xyz" 相同的对象实例。 所以前者其实理解为被后者的所包含。
String和StringBuilder、StringBuffer的区别?
String:String 的值被创建后不能修改,任何对 String 的修改都会引发新的 String 对象的生成。
StringBuffer:跟 String 类似,但是值可以被修改,使用 synchronized 来保证线程安全。
StringBuilder:StringBuffer 的非线程安全版本,没有使用 synchronized,具有更高的性能,推荐优先使用。
String 类可以继承吗?
不行。String 类使用 final 修饰,无法被继承
-
final
Java 中的 final 关键字有哪些用法?
修饰类:该类不能再派生出新的子类,不能作为父类被继承。因此,一个类不能同时被声明为abstract 和 final。
修饰方法:该方法不能被子类重写。
修饰变量:该变量必须在声明时给定初值,而在以后只能读取,不可修改。 如果变量是对象,则指的是引用不可修改,但是对象的属性还是可以修改的。
public class FinalDemo { // 不可再修改该变量的值 public static final int FINAL_VARIABLE = 0; // 不可再修改该变量的引用,但是可以直接修改属性值 public static final User USER = new User(); public static void main(String[] args) { // 输出:User(id=0, name=null, age=0) System.out.println(USER); // 直接修改属性值 USER.setName("test"); // 输出:User(id=0, name=test, age=0) System.out.println(USER); } }
阐述 final、finally、finalize 的区别
其实是三个完全不相关的东西,只是长的有点像。。 final 如上所示。 finally:finally 是对 Java 异常处理机制的最佳补充,通常配合 try、catch 使用,用于存放那些无论是否出现异常都一定会执行的代码。在实际使用中,通常用于释放锁、数据库连接等资源,把资源释放方法放到 finally 中,可以大大降低程序出错的几率。 finalize:Object 中的方法,在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。finalize()方法仅作为了解即可,在 Java 9 中该方法已经被标记为废弃,并添加新的 java.lang.ref.Cleaner,提供了更灵活和有效的方法来释放资源。这也侧面说明了,这个方法的设计是失败的,因此更加不能去使用它。
-
String异常
Error 和 Exception 有什么区别?
Error 和 Exception 都是 Throwable 的子类,用于表示程序出现了不正常的情况。区别在于: Error 表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题,比如内存溢出,不可能指望程序能处理这样的情况。 Exception 表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题,也就是说,它表示如果程序运行正常,从不会发生的情况。
不要在finally中return 数据
public class TryDemo { public static void main(String[] args) { System.out.println(test()); } public static int test() { try { return 1; } catch (Exception e) { return 2; } finally { System.out.print("3"); } } } 结果:31 ,在 return 前会先执行 finally 语句块,所以是先输出 finally 里的 3,再输出 return 的 1 public class TryDemo { public static void main(String[] args) { System.out.println(test1()); } public static int test1() { try { return 2; } finally { return 3; } } } 结果:3 try 返回前先执行 finally,结果 finally 里不按套路出牌,直接 return 了,自然也就走不到 try 里面的 return 了。 public class TryDemo { public static void main(String[] args) { System.out.println(test1()); } public static int test1() { int i = 0; try { i = 2; return i; } finally { i = 3; } } } 结果:2 在执行 finally 之前,JVM 会先将 i 的结果暂存起来,然后 finally 执行完毕后,会返回之前暂存的结果,而不是返回 i,所以即使这边 i 已经被修改为 3,最终返回的还是之前暂存起来的结果2
throw和throes的区别?
(1)throw 作用在方法内,表示抛出具体异常,由方法体内的语句处理; 一定抛出了异常; (2)throws 作用在方法的声明上,表示抛出异常,由调用者来进行异常处理; 可能出现异常,不一定会发生异常;
常见的异常类?
NullPointerException:空指针异常; SQLException:数据库相关的异常; IndexOutOfBoundsException:数组下角标越界异常; FileNotFoundException:打开文件失败时抛出; IOException:当发生某种IO异常时抛出; ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出此异常; NoSuchMethodException:无法找到某一方法时,抛出; ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常; NumberFormatException:当试图将字符串转换成数字时,失败了,抛出; IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。 ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。