传送门
String类可以被继承吗?作为Java语言的开发者为什么把String类定义为final呢?String类在声明的时候使用final关键字修饰,被final关键字修饰的类无法被继承,除此之外String的底层char[ ]也是由final修饰的,因此String具有不可变性质
- 当字符串不可变的时候,字符串池才有可能实现,因为不同的字符串变量都指向池中的同一个字符串,这样可以节省很多堆空间
- 由于字符串是不可变的,所以它是多线程安全的,当一个字符串被多个线程共享的时候,就不用担心线程安全问题了
- 字符串因为拥有不可变的性质,它在创建的时候HashCode就被缓存了,不需要重新计算,这就使得String类型很适合作为HashMap中的键
- final修饰变量,一旦赋值,不可重新赋值
- final修饰方法,此方法不能被重写
- final修饰实例变量,必须手动赋值,不能采用系统默认值,一般与static联用,用来声明常量
- final不能和abstract关键字联合使用
&是逻辑与;&&是短路与;在程序中最终的运算结果完全一致,但是&&存在短路情况,当运算符左边表达式为false的时候,右边的表达式就不会执行了
两个对象值equals结果为true,但是却可以拥有不同的hashcode,这句话是否正确?不正确,如果两个对象满足equals方法,他们的哈希值应当相同,因此我们在重写equals方法的时候,总是要重写hashcode方法,这是java规定的.如果违背了这个原则你会发现在使用集合的时候,相同的元素会出现在set集合中,并且增加新元素的效率会大大下降. 要注意的是当两个对象的hashcode相同的时候,他们两并不一定相同
在java中如何跳出嵌套的循环?-使用标志位public static void main(String[] args) {
outfor: for (int i = 0; i < 10; i++){
for (int j = 0; j < 10; j++){
if (j == 5){
break outfor;
}
System.out.println("j = " + j);
}
}
}
重载和重写的区别?
重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态,后者是运行时的多态
- 重载发生在一个类中,同名的方法如果有不同的参数列表(类型,个数,顺序)则被视为重载,并且重载不呢通过返回值来区分,如下:
public void testMethod(){ doSome();//无法确定调用的方法到底是哪个 } public void doSome(){ } public int doSome(){ return 1; } - 重写发生在父类和子类之间,重写要求子类重写之后的方法与父类被重写的方法拥有相同的返回类型,不能比父类被重写的方法声明更多的异常,访问权限不能比父类更低,并且构造方法不能被重写,final的方法不能重写,static的方法不存在重写
是值传递,java的方法调用只支持参数的值传递,当一个对象实例作为一个参数被传递到方法中的时候,参数的值就是对该对象的内存地址,当这个内存地址被传递后,同一个内存地址指向堆内存中的同一个对象,所以通过哪个引用去操作这个对象,对象的属性都是改变的
抽象类和接口的区别?先说接口java8的改变
- 提供了静态方法和默认方法,分别使用 static 和 default 关键字修饰,这两种方法可以有方法体 (下面是需要注意的地方)。default 方法属于实例,static 方法属于接口或类。要注意的是:default 方法可以被继承,static 方法不会。如果一个类实现了多个接口,并且这些接口之间没有相互继承关系,同时存在相同的 default 方法时会报错,不过你可以在实现类中重写 default 方法并通过 <接口>.super.<方法名>(); 形式指定调用哪个父接口中的 default 方法。
- 如果一个接口只有一个抽象方法,那么这个接口会默认自动变成函数式接口。
- 如果使用了@FunctionalInterface 注解对接口进行修饰,说明这个接口是一个函数式接口,在该接口只能有一个抽象方法 (不限制静态方法和默认方法)。一个函数式接口可以通过 Lambda 表达式来创建该接口的对象。
不同点(java8以前):
- 抽象类可以定义构造器,接口不能
- 抽象类可以有具体的方法和抽象方法,接口不能有具体的方法
- 抽象类的成员可以用public,private,protected,默认等修饰,接口从成员全部都是public(附上每个修饰符的范围表)
- 抽象类可以定义成员变量,接口中只能定义常量
- 抽象类可以包含静态方法,而接口不能
- 一个类只能继承一个抽象类,但是可以实现多个接口
相同点:
- 不能实例化
- 可以将抽象类和接口类型作为引用类型
- 一个类如果继承了某个抽象类或者实现类某个即可都需要对其中的抽象方法全部进行实现否在该类需要被声明为抽象类
都不可以
- 抽象方法需要子类重写,静态方法无法被重写
- native方法是本地代码实现的方法,抽象方法是没有实现的
- synchronized和方法的实现细节有关,抽象方法不涉及任何实现细节
基础区别是一个是运算符一个是方法
- ==:如果比较的对象是基本数据类型和包装类,判断数值是否相等,如果是引用数据类型,比较的是对象的地址值是否相等
- equals:用来判断两个对象的内容是否相等,equals方法不能用于基本数据类型的变量,如果没有对equals方法进行重写,比较的是对象的地址
不管创建多少个对象,静态变量在内存中有且仅有一个,实例变量必须依存于某一个实例,需要先创建对象然后通过对象才能访问到他,静态变量可以实现让多个对象共享内存
String s="Hello";s=s+"world";这两句代码执行以后,初始String对象中的内容是否改变了?没有,String具有不可变性,s虽然现在打印输出了helloword但是是因为s不指向原来的对象了而是指向了另一个对象,由此可以得到启发,在经常对字符串进行修改的话,使用String会引起额外的开销.
此外如果常量池里已经有了helloworld这个字符串,当我们要使用此字符串的时候不需要new String("helloworld"),直接s="helloworld"就行了,对于字符串常量,如果内容相同,Java 认为它们代表同一个 String 对象,而用关键字 new 调用构造器,总是会创建一个新的对象,无论内容是否相同。
面向对象包括哪些特性,如何理解?封装,继承,多态,(抽象)
- 封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口
- 继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)
- 多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情,实现多态必须要做两件事: 使用父类的引用指向子类的对像,子类方法的重写
- 抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面,抽象只关注对象有哪些属性和行为,不关心它具体做什么
Java中为什么要使用clone?什么是浅克隆和深克隆?
在实际编程过程中,我们常常要遇到这种情况:有一个对象 A,在某一时刻 A 中已经包含了一些有效值,此时可能会需要一个和 A 完全相同新对象 B,并且此后对 B 任何改动都不会影响到 A 中的值,也就是说,A 与 B 是两个独立的对象,但 B 的初始值是由 A 对象确定的。在 Java 语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但clone()方法是其中最简单,也是最高效的手段。
- 浅克隆:创建一个新的对象,新对象的属性和原来的完全相同,对于非基本类型的属性,仍然指向原有属性所指向的对象的内存地主
- 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,并且不再指向原有对象地址,其具体方法是在对象的clone方法中,克隆其原有属性
class Teacher implements Cloneable {
private String name;
private Student student;
@Override
public Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
//注意以下代码
Teacher teacher = (Teacher)super.clone();
teacher.setStudent((Student)teacher.getStudent().clone());
return teacher;
}
}
new一个对象和clone一个对象的区别
new操作符本意是分配内存,clone方法被调用之后分配的内存和原对象一致,然后使用原对象的所有域填充到新对象的域,最后返回这个新对象的引用
谈谈对多态的理解
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源代码,就可以让引用变量绑定到各种不同的对象上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
final,finally,finalize的区别?final:用于声明属性,方法和类,表示属性不可变,方法不可覆盖,被修饰的类不可继承
finally:异常处理语句的一部分,表示总是执行
finalize:object类的一个方法,当java对象没有更多的引用指向的时候,会被垃圾回收器回收,该对象被回收之前,由垃圾回收器来负责调用此方法,如果对象在finalize()重新与引用链上的任何一个对象建立关联,该对象就会复活
java的异常的种类,error和exception的区别?Error类和Exception的父类都是Throwable类,区别如下:
Error类一般值得是与虚拟机相关的问题,比如内存空间不足,栈溢出,系统崩溃等等,这类错误导致的应用程序中断,仅靠程序本身无法恢复,遇到这样的错误,建议程序终止
Exception类表示程序可以处理的异常,可以捕获并且可以恢复,遇到这类异常,应该尽可能快的处理,使程序恢复运行,Exception类又分为未检查异常(UnCheckedException)和受检查的异常(CheckedException)
- 编译时异常:CheckedException,编译时异常必须显式的处理,处理的时候要么try catch处理掉要么往上抛出
- 运行时异常:unCheckedException,只有当代码在运行时才发生的异常
- 空指针异常 NullPointerException
- 数组越界异常 IndexOutOfBoundsException
- 类转换异常 ClassCaseException
- 向数组中存放与声明类型不兼容对象异常 ArrayStoreException
- Io操作异常 BufferOverFlowException
- 算术异常ArithmeticException
throw在方法体内,表示抛出异常,由方法体内的语句处理,是具体向外抛的动作
throws用在方法声明后面,表示如果抛出异常就由该方法的调用者来进行异常处理,throws表示出现一种异常的可能性,并不一定会发生这种异常
Math.round(11.5)等于多少?Math.round(- 11.5) 又等于多少?Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行取整。
switch case语句可以作用在哪些变量上?char int byte short enum
String,StringBuilder,StringBuffer的区别?String是只读字符串,StringBuffer/StringBuilder 表示的字符串对象可以直接进行修改,StringBuffer线程安全,StringBuilder效率更高
以下程序的输出?public static void main(String[] args) {
String s1 = "Programming";
String s2 = new String("Programming");
String s3 = "Program";
String s4 = "ming";
String s5 = "Program" + "ming";
String s6 = s3 + s4;
System.out.println(s1 == s2); //false
System.out.println(s1 == s5); //true
System.out.println(s1 == s6); //false
System.out.println(s1 == s6.intern()); //true
System.out.println(s2 == s2.intern()); //false
}
● String 对象的 intern()方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String 对象的 equals 结果是 true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;
● 字符串的+操作其本质是创建了 StringBuilder 对象进行 append 操作,然后将拼接后的 StringBuilder 对象用 toString 方法处理成 String 对象,这一点可以用 javap -c StringEqualTest.class 命令获得 class 文件对应的 JVM 字节码指令就可以看出来。
如何获取年月日,时分秒?如何获取1970年1月1日到现在的毫秒数? public static void main(String[] args) {
// Java 8
LocalDateTime dt = LocalDateTime.now();
System.out.println(dt.getYear());
System.out.println(dt.getMonthValue()); // 1 - 12
System.out.println(dt.getDayOfMonth());
System.out.println(dt.getHour());
System.out.println(dt.getMinute());
System.out.println(dt.getSecond());
}
public static void main(String[] args) {
System.out.println("第一种:" + Calendar.getInstance().getTimeInMillis());
System.out.println("第二种:" + System.currentTimeMillis());
System.out.println("第三种:" + Clock.systemDefaultZone().millis());
}
如何获取某月的第一天和最后一天?如何格式化日期?
public static void main(String[] args) {
//Java 8
LocalDate today = LocalDate.now();
//本月的第一天
LocalDate firstday = LocalDate.of(today.getYear(), today.getMonth(), 1);
//本月的最后一天
LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("本月的第一天" + firstday);
System.out.println("本月的最后一天" + lastDay);
}
public static void main(String[] args) {
// Java 8
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date2 = LocalDate.now();
System.out.println(date2.format(newFormatter));
}
打印昨天的当前时刻?
public static void main(String[] args) {
LocalDateTime today = LocalDateTime.now();
LocalDateTime yesterday = today.minusDays(1);
System.out.println(yesterday);
}
Java各种数据类型都占用几个字节?String是什么数据类型?
byte short int long float double boolean char--12484812,String是引用数据类型
short s1 = 1; s1 = s1 + 1; 有错吗?short s1 = 1; s1 += 1 有错吗?- 前者有错,后者无错,由于 1 是 int 类型,因此 s1+1 运算结果也是 int 型,需要强制转换类型才能赋值给 short 型
- short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short)(s1 + 1);其中有隐含的强制类型转换
java是一个完全面向对象的语言,但是为了编程的方便还是引入了基本数据类型,为了能够将这些基本数据类型当成对象操作,java引入了每个基本数据类型的包装类,并且在java5引入了自动装箱和拆箱机制,使得二者可以互相转换
以下代码的输出结果?public static void main(String[] args) {
Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
System.out.println(f1 == f2);//true
System.out.println(f3 == f4);//false
}
如果整型字面量的值在-128 到 127 之间,那么不会 new 新的 Integer 对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1==f2的结果是 true,而f3==f4 的结果是false。
数据类型之间如何转化?字符串转基本数据类型: 调用基本数据类型对应的包装类中的方法 parseXXX(String)或valueOf(String)即可返回相应基本类型。
基本数据类型转字符串:一种方法是将基本数据类型与空字符串(“”)连接(+)即可获得其所对应的字符串;另一种方法是调用 String类中的 valueOf()方法返回相应字符串。
附上String类的api:
| char charAt(int index) | 返回指定索引处的 char 值。 |
| boolean contains(CharSequence s) | 当且仅当此字符串包含指定的 char 值序列时,返回 true。 |
| boolean endsWith(String suffix) | 测试此字符串是否以指定的后缀结束。 |
| boolean equals(Object anObject) | 将此字符串与指定的对象比较。 |
| boolean equalsIgnoreCase(String anotherString) | 将此 String 与另一个 String 比较,不考虑大小写。 |
| byte[] getBytes() | 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。 |
| int indexOf(String str) | 返回指定子字符串在此字符串中第一次出现处的索引。 |
| int length() | 返回此字符串的长度。 |
| String replaceAll(String regex, String replacement) | 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 |
| String[] split(String regex) | 根据给定正则表达式的匹配拆分此字符串。 |
| boolean startsWith(String prefix) | 测试此字符串是否以指定的前缀开始。 |
| String substring(int beginIndex) | 从beg开始返回字符串 |
| String substring(int beginIndex, int endIndex) | 根据[beg,end)返回字符串 |
| char[] toCharArray() | 将此字符串转换为一个新的字符数组。 |
| String toLowerCase()/String toUpperCase() | 大小写转化 |
| String trim() | 返回字符串的副本,忽略前导空白和尾部空白。中间的不处理 |



