-
String
final类,不可被继承。
一旦被初始化就不可变。 -
StringBuffer
可以直接修改字符串对象,线程安全 -
StringBuilder
去掉synchronized的StringBuffer。
非线程安全,但单线程里效率比StringBuilder高。
char数组缓冲区,默认16,2倍扩容
静态常量池,即class文件中的常量池,class文件中的常量池不仅仅包含字符串字面量,符号引用,还包含类、方法的信息,占用class文件绝大部分空间。
运行时常量池,则是jvm虚拟机在完成了装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
String s1 = new String("a");
创建了两个对象。
1、在class被ClassLoader加载时,"a"被作为常量读入,放进了常量池。
2、new String()是从常量池复制来的新的String对象,被放进堆中。
对象引用s1指向堆中的"a"。
String s1="123";
String s2="1"+"23";//编译时常量拼接,优化为"123"
final String s3="1";
String s4=s3+"23";//因为s3是常量,所以可以优化为"123"
//s1==s2 --> true
//s1==s4 --> true
intern() 会得到字符串对象在常量池中引用,如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中的引用。
System.out.println(new String("a")=="a");//false
System.out.println(new String("a").intern()=="a");//true
因为用+进行字符串连接会创建多个String对象,所以jdk1.5开始,带有字符串变量的连接操作,jvm内部采用的是StringBuilder来实现的,而之前这个操作是采用StringBuffer实现的。
Integer a = null;
String b = a+"123";
//b--> "null123"
装箱:new StringBuilder().append(a).append("123").toString();
StringBuilder的append(Object obj)方法调用了String.valueOf(Object obj),将null转变为字符串。
public static String valueOf(Object obj){
return (obj==null)?"null":obj.toString();
}
- 拆箱装箱
拆箱:i.intValue(); (i.xxValue())
装箱:Integer.valueOf(1); (Xx.valueOf())
"==" 若有一个操作符是表达式,则自动拆箱比较数值。
3+""//String.valueOf(3)+""
i==i+1;//i.intValue() == i.intValue()+1
i==1;//i.intValue() == 1
- Integer缓存
数值在[-128,127]之间,Integer会缓存起来,通过valueOf方法创建对象时,如果缓存中存在对象引用,返回的是缓存的对象而不会新new一个对象。
- 类加载
加载、链接和初始化。链接分为校验、准备和解析三步。
加载,校验,准备需要在初始化之前开始。- 加载:由类加载器完成,查找和读入类或接口的二进制数据(如class文件),然后转化为方法区的数据结构,一个代表这个类的Class对象,。
- 校验:检查读入的数据是否符合jvm的要求,并且不会危害jvm的安全。
- 准备:给类的静态变量分配内存并设置默认的初始值。
- 解析:将符号引用转成直接引用(将class常量池的类名等转化为确切的内存地址)。(该阶段可在初始化后再开始,为了支持运行时绑定)
在解析类的全限定名的时候,调用当前类的ClassLoader来加载它并拿到引用?? - 初始化:按顺序执行类定义中java程序静态代码。编译器编译时已经将所有类变量和静态代码块都收集在了一个静态代码块中。
虚拟机规范没有限制什么时候进行类加载但对初始化有严格的规定。
初始化触发条件如下
Java中类的初始化
1. new时、读取或设置一个类变量(常量除外)时、调用类的静态方法时。即遇到new、getstatic、putstatic、invokestatic这四条字节码指令时如果类还没初始化,则需要先触发其初始化。
2. 使用java.lang.reflect包的方法反射调用类时。
3. 当初始化一个类时如果发现其父类还没进行初始化,则需要先触发其父类的初始化。(接口除外)
4. main方法所在的类会自动被jvm初始化。
以上对类的主动引用会触发初始化,其他所有引用类的方式不会触发初始化,称为被动引用。
1. 通过子类引用父类中的静态字段,子类不会被初始化,但父类
2. 数组定义不会初始化该类。它的字节码指令是newarray,是对数组引用类型的初始化,数组引用类型由虚拟机自动生成。
3. 常量在编译阶段会存入调用它的类的常量池中,本质没有直接引用到定义该常量的类,因此不会触发定义常量的类的初始化。
-
接口的加载过程
接口中不能使用”static{}“语句块,但是编译器会为接口生成”()”类构造器,用于初始化接口中所定义的成员变量。
接口在初始化的时候,并不要求其父接口都完成了初始化,只要在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化。 -
对象的初始化顺序
- 父类静态内容
- 子类静态内容
- 父类构造代码块(编译器会将构造代码块插入到每个构造方法的最前端)
- 父类构造方法
- 子类构造代码块
- 子类构造方法
静态成员无法引用this和super,因为不存在调用它们的实例。
-
.class 和 Class.forname() 都可以获得class对象但是.class不会初始化而Class.forname()会
-
反射 Field的setAccessible(true)能让反射对象在使用时抑制java语言访问检查,也就是能够获得private属性的变量。
-
动态加载在Java中有2种实现,第一种通过ClassLoader.loadClass()去加载,ClassLoader最高级别的类加载器,所有的类都归他管理,用loadClass加载的类,是未经过初始化的,所以很多静态变量和方法无法被使用,第二种方法则是,Class.forName().用这个方法加载的类已经经过初始化。
-
关于switch
Java常考面试题5 --switch的表达式考察
题:switch的表达式是否可以为long?是否可以为string?
答:
在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。
从Java 5开始,Java中引入了枚举类型,expr也可以是enum类型,
从Java 7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。 -
重载
多态性的一种表现,方法名相同,参数类型不同,顺序不同。
其他相同返回值不同IDE会报错,提示存在相同的方法。
public void run(int i, char x){System.out.println("int,char");}
public void run(char x,int i){System.out.println("char,int");}
-
重写
父类和子类的多态性表现,对父类的函数进行重新定义。
子类函数的访问修饰权限范围不能小于父类的。
抛出的异常不能是父异常 -
Exception
Throwbale<-- Error/Exception ;Exception <-- RuntimeException/xxException(checkedException)uncheckedException 未检查异常,就是RuntimeException运行时异常,要么不可控制要么就应该避免发生,这类异常运行时才会被检测到,可以不用抛出不用捕获
- 错误的类型转换 ClassCastException
- 数组访问越界
- 空指针
- ......
checkedException 已检查异常,必须抛出或者捕获,IOException,InterruptedException等
Error 错误 java运行时系统的内部错误和资源耗尽错误。 -
IO
- 字节流 OutputStream/InputStream
- 字符流 Writer/Reader <-- InputStreamReader/OutputStreamWriter
通过InputStreamWriter和OutputStreamWriter进行字节流和字符流的转换
https://blog.csdn.net/u012467492/article/details/52972916
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IOTest {
/**
* 字符流的写入
* @param file
* @param content
*/
public void writeFileByWriter(File file, String content) {
FileWriter o = null;
try {
o = new FileWriter(file);// FileWriter继承了OutputStreamWriter
o.write(content);
o.flush();// 将缓冲区数据写入目标文件中
} catch (IOException e) {
e.printStackTrace();
} finally {
if (o != null) {
try {
o.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 字符流的读取文件
* @param file
*/
public void readFileByReader(File file) {
FileReader fr = null;
try {
fr = new FileReader(file);
// int ch = 0;
// while ((ch = fr.read()) != -1) {// read()返回一个字符
// System.out.println((char) ch);
// }
char[] buf = new char[1024];
int len = 0;
while ((len = fr.read(buf)) != -1) {// 读一串进char数组中,返回读的数量
System.out.println(new String(buf, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
*
* @param source
* @param target
*/
public void copyFileByCharBuffer(File source, File target) {
BufferedReader bi = null;
BufferedWriter bo = null;
try {
bi = new BufferedReader(new FileReader(source));
bo = new BufferedWriter(new FileWriter(target));
String line = null;// readLine()读取行,只返回换行符之前的数据
while ((line = bi.readLine()) != null) {
bo.write(line);
bo.newLine();// 换行
}
bo.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {// 关闭缓冲区时,缓冲区会顺带关闭它的流。
if (bi != null) {
try {
bi.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bo != null) {
try {
bo.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 字节流的写入
* @param file
* @param content
*/
public void writeFileByStream(File file, String content) {
FileOutputStream o = null;
try {
o = new FileOutputStream(file);
byte[] buf = content.getBytes();
o.write(buf);// write(buf)字节数组
// o.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (o != null) {
try {
o.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 字节流的读取文件
* @param file
*/
public void readFileByStream(File file) {
FileInputStream i = null;
try {
i = new FileInputStream(file);
// int ch = 0;
// while ((ch = i.read()) != -1) {// read()返回一个字节byte
// System.out.println((char) ch);
// }
byte[] buf = new byte[1024];
int len = 0;
while ((len = i.read(buf)) != -1) {// 读一串进byte数组中,返回读的数量
System.out.println(new String(buf, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (i != null) {
try {
i.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
*
* @param source
* @param target
*/
public void copyFileByStreamBuffer(File source, File target) {
BufferedInputStream bi = null;
BufferedOutputStream bo = null;
try {
bi = new BufferedInputStream(new FileInputStream(source));
bo = new BufferedOutputStream(new FileOutputStream(target));
int len = 0;
byte[] buf = new byte[1024];
while ((len = bi.read(buf)) != -1) {
bo.write(buf,0,len);
}
bo.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {// 关闭缓冲区时,缓冲区会顺带关闭它的流。
if (bi != null) {
try {
bi.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bo != null) {
try {
bo.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
-
private 方法默认为final
-
comparable和comparator的区别
https://blog.csdn.net/tolcf/article/details/52229068
对象实现接口comparable(只有一个public int compareTo(T o);
方法)
比较器实现接口comparator,类外部自定义比较规则用,不影响类,灵活
在用Collections类的sort方法排序时若不指定Comparator,那就以自然顺序排序。所谓自然顺序就是实现Comparable接口设定的排序方式。
一个已经实现comparable的类的对象或数据,可以通过Collections.sort(list) 或者Arrays.sort(arr)实现排序。通过Collections.sort(list,Collections.reverseOrder());对list进行倒序排列。 -
foreach jvm需要把它转成iterator,然后每次遍历都要调用hasnext。性能比较差,数据量很多很多时就看得出来。for循环只需每次加1。
- 面向对象4个特征:
- 封装:把事务的方法和属性放在类中
- 抽象:抽出相同之处
- 继承:子类共享父类数据
- 多态:父类引用在运行期间才确定调用哪个子类实例对象的方法。不修改代码就可让程序有多个运行状态可选择。
- Math.round(x) => Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整
- 一个".java"源文件中可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。
- 访问权限修饰符protected 允许子类访问
- interface 变量默认为public static final 类型,方法默认为public abstract类型
- native 本地。表示该方法要用另外一种依赖平台的编程语言实现。
- synchronized 使用的同步锁对象是this。抽象方法无法确定this是什么。
- 内部类不能定义静态成员,可以访问外部类中的成员变量(静态内部类不可以)。方法内定义的内部类类似局部变量不能有访问修饰符,但可以有final和abstract
- final修饰方法,子类不能覆盖该方法但可以继承?继承逻辑是什么?创建子类对象是会先调用父类构造方法
- String是final类型,不可继承修改,被设计成不可变类,所有对象都是不可变对象。提高效率用StringBuffer
- StringBuffer没有覆盖equals方法和hashcode方法,所以将其存进集合类(hashmap等)中会出现问题
- 字符串常量缓冲区,new String(“123”)字符串常量缓冲区看有无“123”,没有创建“123”对象,有直接从缓冲区拿“123”,用来创建新的String对象
- 编译优化:String a = "1"+"2"+"3";==>String a = "123";
- finally和return谁先?try的return表达式执行后再执行finally,如果finally有return最后返回的是finally的。
网友评论