框架是个好东西,可早晚有一天会过时,这世界上就没有亘古不变的东西,来学下Java基础吧
常量池的概念
Byte,Short,Integer,Long,Character,Boolean的范围为[-128,127]的缓存数据
public static void main(String[] args) {
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1==i2);
}
<<<true
public static void main(String[] args) {
Integer i1 = 128;
Integer i2 = 128;
System.out.println(i1==i2);
}
<<<false
+运算
public static void main(String[] args) {
Integer i1 = 111;
Integer i2 = 100;
Integer i3 = 11;
System.out.println(i1 ==i2+i3);
}
<<<true
Float,Double并没有实现常量池技术
String类型
只要有new
就直接在堆内存空间创建一个新的对象
public static void main(String[] args) {
String i1 = "aaa";
String i2 = "aaa";
System.out.println(i1 ==i2);
}
<<<true
public static void main(String[] args) {
String i1 = new String("aaa");
String i2 = new String("aaa");
System.out.println(i1 ==i2);
}
<<<false
如果没有new则直接从常量池中拿
+运算【相当于是新建了个对象来接收i2+i3】
public static void main(String[] args) {
String i1 = "aaa";
String i2 = "aa";
String i3 = "a";
System.out.println(i1 ==i2+i3);
}
<<<false
包装类具有自动装箱和拆箱的功能
即将基本类型自动和封装类型相互转换
String和StringBuffer,StringBuilder
String一部分
public final class String implements Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value;
}
被final修饰,不可变
StringBuffer和StringBuilder【父类都是AbstractStringBuilder】
abstract class AbstractStringBuilder implements Appendable, CharSequence {
byte[] value;
}
没有被final修饰,可变
线程安全问题:
- 为什么会出现线程安全问题?
多个线程同时操作一片内存资源,导致信息不对称
public class Counter {
protected long count = 0;
public void add(long value){
this.count = this.count + value;
}
}
如果A线程和B线程同时调用 counter.add(2); 那么最后count可能还是2
- String对象是不可变的,是常量,不存在线程安全问题
- StringBuffer对字符串的操作加了锁,是线程安全的
- StringBuilder没有加锁,是非线程安全的
性能:
因为每次操作String都会生成一个新的对象,再将指针指向新对象,性能较低,StringBuffer性能次之,StringBuilder性能最好,确是非线程安全的
使用规则:
- 少量数据用String
- 单线程操作大量数据,用StringBuilder
- 多线程操作大量数据,用StringBuffer
⚠️ 在静态方法中不能调用其他非静态变量,比如this
接口和抽象类的区别
- 方法上
接口方法默认是public,而且在Java8之前不能有实现【Java8使用default关键字】
抽象类可以是public,protected,private,可以有非抽象方法
- 变量上
接口中只能存在static,final的变量
抽象类可以有非static,final的变量
- 设计层面
抽象类是对类的抽象
接口是对行为的抽象
⚠️ 抽象类的静态方法可以通过实现类.方法名来调用;Java8之后接口也能定义静态方法,只能通过接口名.方法名来实现,不能通过实现类.函数名来调用
成员变量和局部变量
- 语法形式
成员变量和局部变量都可以被final修饰,但只有成员变量能被static修饰
- 变量的内存存储形式
堆:存放所有new出来的对象【包含常量池】。
堆区也叫静态区,存放整个程序中唯一的元素,例如类和sttaic变量
栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(对象可能在常量池里)(字符串常量对象存放在常量池中。)
静态成员变量存放于堆区,普通成员变量也存在于堆区【创建的对象在堆区】
成员变量存在于栈
new一个对象的全过程
1.对象被new出来后,实体存在于堆区,对象的引用存放于栈区
2.但对象里面的方法只是声明了,等到对象调用方法的时候,再到栈区去创建方法的实现
- 变量的初值问题
成员变量会以类型的默认值来赋值
⚠️包装类不会自动赋初值,只有基本的数据类型会
⚠️ 被声明成final的变量必须显式赋值
局部变量不会自动赋初值
==和equals的区别
==
如果两个操作数是基本数据类型则比较数值,如果是引用数据类型则比较的是内存地址
equals
如果类重写了equals方法,一般会将equals会实现对两个对象内容的判断
如果没重写,则 等价于使用==
hashcode与equals的区别【还没搞懂】
hashcode是为了获取哈希码【散列码】,返回一个int整数。哈希码的作用是确定对象在哈希表中的索引位置
哈希表存储的是键值对,能快速的通过key查找到value
Java中只有值传递,没有引用传递
这是与其他的面向对象的语言不同的地方
即使将对象的引用为参数往方法里传,本质上传的是对象在堆内的地址
举个栗子
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s1 = new Student("小张");
Student s2 = new Student("小李");
Test.swap(s1, s2);
System.out.println("s1:" + s1.getName());
System.out.println("s2:" + s2.getName());
}
public static void swap(Student x, Student y) {
Student temp = x;
x = y;
y = temp;
System.out.println("x:" + x.getName());
System.out.println("y:" + y.getName());
}
}
<<< x:小李
<<< y:小张
<<< s1:小张
<<< s2:小李