本文主要是收录一些人们可能经常会忽略,但又挺重要的Java方面的小知识。有推荐的也可以在下方留言,大家一起进步呀!
目录:
1.a=a+b和a+=b的区别
2.String str1 = new String("a"); String str2 = "a"+"b"; 这两行代码分别创建了几个对象,是什么
3.启动线程的方式以及如何更好地结束一个线程?
4.Java 中 == 和 equals 和 hashCode的区别
5.Java浮点数的默认类型是什么
6.多态成员访问特点
7.用来实现线程间通知和唤醒的方式
8.初始化过程
9.值传递和引用传递
10.线程共享的内容和私有的内容
11.Thread和Runnable的区别
12.迭代器遍历元素的时候,通过集合是不能修改元素的
13.EditText中的值为手机号时不能转为int
2019-7-7
1.a=a+b和a+=b的区别
(1)从效率上看
a+=b要略高于a=a+b
(2)从运算符上看
a+=b的执行过程是先计算出a的值,然后用一个temp对象存储,之后和b进行相加,然后将值赋给a引用。
a=a+b的执行过程是先计算a+b,然后再赋值给a引用,赋值给a引用的时候如果引用a有计算过程,则会再次计算。
(3)从类型自动转换上看
当a和b的数据类型不一样时,a+=b会自动进行类型转换;而a=a+b不会,需要手动进行类型转换
比如:
byte a = 1;
int b = 4;
a = a + b;//此时编译会报错,因为a为byte,b为int,
//a+b的时候默认会向大类型即int转换,则a+b的类型为int,而a为byte,所以报错
a += b;//此时编译不会报错,因为+=会自动进行类型转换,并且向低转换
2.String str1 = new String("a"); String str2 = "a"+"b"; 这两行代码分别创建了几个对象,是什么
(1)首先第一行代码中创建了两个对象(因为字符串池中没有a字符串),一个是new String创建出来的str1对象,一个是在字符串池中创建的a对象。
(2)然后因为a字符串已经在常量池中存在了,所以在第二行代码中就不会再创建a对象了。接着创建的就是b对象,最后是str2对象,值为ab。
总结:要先看字符串池中有没有该字符串,有的话会直接拿过来,没有再重新创建。
3.启动线程的方式以及如何更好地结束一个线程?
(1)启动线程的方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口通过FutureTask包装器来创建Thread线程
- 匿名内部类
(2)如何更好地结束一个线程
线程中调用interrupt()方法仅仅是在当前线程中打了个停止的标记,并不是真的停止线程。
如何判断线程的状态是否是停止,主要包含以下两种方法:
1、this.interrupted():测试当前线程是否已经是中断状态,执行后具有将状态标志置清除为false的功能。
2、this.isInterrupted():测试线程thread对象是否已经是中断状态,但不具备清除状态标志。
注:使用stop()方法停止线程是非常暴力的,该方法已经作废,不考虑。
注意:官方文档中说明If this thread is blocked in an invocation of the [
wait()
], [wait(long)
], or [wait(long, int)
] methods of the [Object
] class, or of the [join()
], [join(long)
], [join(long, int)
], [sleep(long)
], or [sleep(long, int)
], methods of this class, then its interrupt status will be cleared and it will receive an [InterruptedException
].
也就是说,在run方法里面调用Object类的wait或线程的join/sleep方法之后,这个标记会被清空并抛出InterruptedException异常。解决方法:可在调用的方法的catch中再this.interrupt一次。(不是下文例子中的catch)
① 抛异常法
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread();
t1.start();
Thread.sleep(100);
t1.interrupt();
}
public static class MyThread extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 5000000; i++) {
if (this.isInterrupted()) {
System.out.println("Thread Stop!");
throw new InterruptedException();
}
System.out.println("i: " + i);
}
System.out.println("我在for下面");
} catch (InterruptedException e) {
System.out.println("进入到catch了!");
}
}
}
运行结果:
...
i: 11759
i: 11760
i: 11761
i: 11762
Thread Stop!
进入到catch了!
② 使用return
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread();
t1.start();
Thread.sleep(100);
t1.interrupt();
}
public static class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5000000; i++) {
if (this.isInterrupted()) {
System.out.println("Thread Stop!");
return;
}
System.out.println("i: " + i);
}
System.out.println("我在for下面");
}
}
运行结果:
...
i: 12301
i: 12302
i: 12303
i: 12304
i: 12305
Thread Stop!
③ 使用退出标志
和上面两种方法差不多,只不过是用一个布尔变量代替this.isInterrupted(),然后要想停止线程,赋值该布尔变量为false即可。
2019-8-2
4.Java 中 == 和 equals 和 hashCode的区别
(1)==
对于八种基本数据类型,比较的是他们的值
对于引用数据类型(类,接口和数组),比较的是他们在内存中的地址,所以,除非是同一个new出来的对象,他们比较之后的结果为true,否则为false。eg:
Integer a = new Integer(127);
Integer b = 127;
Integer c = 127;
//a==b为false,b==c为true
Integer d = 128;
Integer e = 128;
//d==e为false
//这里为什么b==c为true而d==e为false呢?
//Integer b = 127在编译时会被翻译成Integer.valueOf(127),该方法对于[-128,127]之间
//的数会进行缓存,如果已经有的话会直接从缓存中拿出,不会再new
//因为128不在该区间,那么d和e就只是new出来的两个不同的对象了,所以就为false
Java中堆和栈的区别
最主要的区别就是:new出来的对象和数组是存放在堆中的,基本数据类型的变量和对象的引用变量是存放在栈中的。
Java的内存机制:
栈:当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当该变量超过它的作用域后,Java会自动释放掉为该变量所分配的内存空间。
堆:在堆中分配的内存,由GC来管理。在堆中产生了一个数组或对象之后,还可以在栈中定义一个特殊的变量,让栈中这个变量的值等于该数组或者对象在堆内存中的首地址,栈中的这个变量就成了数组或者对象的引用变量,以后可以在程序中使用栈中的引用变量来访问数组或者对象。
(2)equals
默认情况下的equals方法是没有被覆盖的,调用的都是Object类的equals方法,这种情况下的equals方法和==是等效的。
public boolean equals(Object obj) {
return (this == obj);
}
如果equals方法被覆盖,则按照新的方法来判断,覆盖后一般
都是通过对象的内容是否相等来判断对象是否相等。比如String类的equals方法:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
String类的equals方法总结为:
首先看是不是同一个对象,是的话返回true
然后看对比对象是不是String类型,
接着看长度是否一致
最后比较逐个字符是否相同
实现高质量的equals方法的技巧如下:
首先比较两个是否==,
然后检查对比对象是否是该类型,
接着比较他们的域是否相同
最后检查对称性,传递性,一致性
(3)hashCode
hashCode()方法也是Object类的一个方法,返回的是一个int型的整数。在集合类操作中使用,主要是为了提高查询速度。
两者的hashCode相同,他们不一定equals。
两者的hashCode不相同,他们一定不equals。
他们equals,hashCode一定相同。
他们不equals,hashCode有可能相同。
2019-8-9
5.Java浮点数的默认类型是什么
答案:double。
6.多态成员访问特点
成员变量:编译看左边,运行看左边
静态变量:编译看左边,运行看左边
成员方法:编译看左边,运行看右边
静态方法:编译看左边,运行看左边
7.用来实现线程间通知和唤醒的方式
① Object.wait/notify/notifyAll
② Condition.await/signal/signalAll
2019-8-10
8.初始化过程
① 首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;
② 然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化;
③ 其次,初始化父类的普通成员变量和代码块,在执行父类的构造方法;
④ 最后,初始化子类的普通成员变量和代码块,在执行子类的构造方法;
比如:
class C {
C() {
System.out.print("C");
}
}
class A {
C c = new C();
A() {
this("A");
System.out.print("A");
}
A(String s) {
System.out.print(s);
}
}
class Test extends A {
Test() {
super("B");
System.out.print("B");
}
public static void main(String[] args) {
new Test();
}
}
Output:
CBB
(1)初始化父类的普通成员变量和代码块,执行 C c = new C(); 输出C
(2)super("B"); 表示调用父类的构造方法,不调用父类的无参构造函数,输出B
(3) System.out.print("B");
所以输出CBB
2019-8-23
9.值传递和引用传递
“在Java里面参数传递都是按值传递”这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递。
简单的说,
基本类型是按值传递,方法的实参是一个原值的副本。
类对象是按对象的引用地址(内存地址)传递地址的值,那么在方法内对这个对象进行修改是会直接反应在原对象上的(或者说这两个引用指向同一内存地址)。
但是String类除外,其规则和基本类型是一样的,因为String是final关键字修饰的,不可变的
eg:
public static void main(String[] args){
String x = new String("LongSh1z");
change(x);
System.out.println(x);
}
public static void change(String x){
x = "changed";
}
输出结果应该是LongSh1z,而不是changed。
解释如下:
当执行到change方法时,Main就入栈,change开启一片栈空间,x是形参,在change的栈内有一份拷贝引用x。(这时Main栈上的x和change栈上的是指向同一片堆空间,即“LongSh1z”)。接着执行x = "changed",此时change栈上的x会指向新的一片空间(方法区上的常量池),而Main栈上的x没有改变。
还不懂的同学可以去看看这一篇,讲的巨详细
https://blog.csdn.net/bntx2jsqfehy7/article/details/83508006
2019-9-5
10.线程共享的内容和私有的内容
(1)线程共享的内容包括:
进程代码段
进程 数据段
进程打开的文件描述符
信号的处理器
进程的当前目录
进程用户 ID 与进程组 ID
(2)线程独有的内容包括:
线程 ID
寄存器组的值
线程的堆栈
错误返回码
线程的信号屏蔽码
2019-9-17
11.Thread和Runnable的区别
(1)Runnable方式可以避免Thread方式由于Java单继承的特性带来的缺陷
(2)Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源(这里的同一资源指的是同一个Runnable对象)的情况
2019-10-26
12.迭代器遍历元素的时候,通过集合是不能修改元素的
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(6);
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
list.add(6);
System.out.println(iterator.next());
}
//会报Exception in thread "main" java.util.ConcurrentModificationException异常
//正确做法:
//①使用ListIterator(List集合特有的迭代器。该迭代器继承了Iterator迭代器)
ListIterator iterator= list.listItertor();
while(lit.hasNext()){
String s = (String)lit.next();
if(s.equals("java")){
iterator.add("love");
//注意:此处是利用迭代器进行添加元素,刚添加的元素处于刚才迭代的元素的后面。
//!!!是iterator添加,不是list添加。!!!
}
}
//②使用普通循环方法,即使用get()方法
for(int i = 0;i<list.size();i++){
String s = (String)list.get(i);
if(s.equals("java")){
list.add("love");//注意:此处是将新的元素添加到了集合的最后
}
}
//③要想继续使用Iterator而不是ListIterator ,可以将ArrayList改为CopyOnWriteArrayList
2019-10-27
13.EditText中的值为手机号时不能转为int
很多时候我们都需要拿到editText里面的值,并且转为int,但是每次Integer.parseInt()时拿到的值都为0。
参照了网上的方法(比如转化之前判断呀,try-catch呀,在监听器里面写呀等等)都没用。
网上的方法可以点击这个看看:http://blog.sina.com.cn/s/blog_a4dda2480101asy3.html
但是最重要的如果editText里面的是String类型的手机号码转为int的话,可以将int改为long。
因为在Java中int的范围是从-2147483648到2147483647(10位),即2的31次方,而手机号码为11位
网友评论