JVM的整体结构主要由三个部分组成:
运行时数据区可以被划分为5个主要组件:方法区、堆区、栈区、PC寄存器以及本地方法栈。其中方法区、堆区与栈区是其三大核心部分。
方法区存储了所有类级别的数据,包括静态变量。每个JVM中只能有一个方法区,其中所有资源共享,故不是线程安全的。
2. 堆区(Heap Area)
所有被创建的对象及其实例变量以及数组都被存放在堆区中,每个JVM同样也只有一个堆区,其内存被多个线程共享,故不是线程安全的。
3. 栈区(Stack Area)
所有的局部变量都被存储在栈区中,每个线程都有自己的运行时栈,因而栈区是线程安全的。
4. PC寄存器
每个线程都有一个单独的PC寄存器来保存当前执行指令的地址,当指令被执行后,PC寄存器便会更新至下一条指令的地址。5. 本地方法栈
所有的本地方法都被保存在这里,每个线程都有自己的本地方法栈。
对于静态变量而言,它是属于类的,因而随着类的创建而被创建,在类的加载过程中,JVM中只会被分配一次内存空间;对于实例变量而言,由于每创建一个对象JVM都会为其分配成员变量内存空间,而实例变量是属于实例对象的,因而创建了几个对象,JVM就会为实例变量分配几次内存空间。
数组中没有length()方法,但是有length()的属性;而String类中有length()方法。
注: 数组中没有length()方法的原因是因为在定义数组时,数组的长度已经是固定的了,length在类中是public final的,因而也就无需设置一个方法在运行是获取数组的长度。
(Since arrays are fixed length defined at the time they are instantiated, length is public final field on the class. There is no need to make it a method since there is no calculation to be done at runtime.)
总而言之:
对于String str = "i"而言,它首先会在字符串常量池中查找i的存在,如果不存在,则在常量池中开辟一块内存空间并将str指向该地址;而对于String str = new String("i")而言,它首先会在对内存中开辟一块内存空间存放str,随后在字符串常量池中查找i,如果不存在则会在常量池中开辟第二块内存空间,并将i指向该地址,也就是相当于新建了两个对象。
public static String reverse(String s){
String res = "";
for(int i = 0; i < s.length(); ++i){
reverse = s.charAt(i) + reverse;
}
return reverse;
}
public static String reverseCharArrays(String s){
char []array = s.toCharArray();
String reverse = "";
for(int i = array.length -1 ; i>=0 ; i--){
reverse +=array[i];
}
return reverse;
}
public static String reverseStringBuffer(String s){
StringBuffer sb = new StringBuffer(s);
String afterReverse = sb.reverse().toString();
return afterReverse;
}
public static String reverseRecursive(String s){
int length = s.length();
if(length<=1){
return s;
}
String left = s.substring(0,length/2);
String right = s.substring(length/2 ,length);
String afterReverse = reverseRecursive(right)+reverseRecursive(left);
return afterReverse;
}
public static void main(String[] args){
StringBuffer buffer = new StringBuffer(“123456”);
System.out.println(buffer.toString());
buffer.replace(0, 1, “a”);
System.out.println(buffer.toString());
}
错误。java中浮点数的默认精度为double,将双精度的double赋值给浮点型float属于下转型,会造成精度丢失,因此需要进行强转,如:float f = (float) 3.4
或者float f = 3.4F
。
二者的区别在于:+=会隐式的将加的结果类型强制转换为持有结果的类型。如:
byte a = 127;
byte b = 127;
b = a + b; //error: cannot convert from into byte
b += a;
在方法的调用过程中,实参将其真实值传递给形参,该传递过程相当与将实参的复制一份传递到函数,对形参的操作不会影响到实参。
引用传递弥补了值传递的不足,当数据量传输过大时,值传递会占用大量的内存空间。而引用传递则是将对象的地址值传递给函数,函数接收的是真实值,即对形参的操作会影响到实参。
反射指的是在运行状态中,对于任意一个类都可以获得其中的属性与方法;对于任意一个对象,都可以调用其中的任意方法。这种动态获取信息以及动态调用对象方法的功能便是Java中的反射。通过反射机制使得代码更加通用
以及灵活
,比如Spring/SpringBoot、MyBatis等框架大量用到了反射机制。
Java中的元注解包括:@Override
、@Deprecated
、@SuppressWarnings
,分别用于标注重写类或方法、类或方法已过时和忽略警告。
注解通常用作对代码进行说明,可以标注在包、类、接口、字段、方法参数、局部变量等。
Spring、SpringMVC以及单元测试等都用到了大量的注解。