美文网首页
那些你所不知道的Java小知识(持续更新)

那些你所不知道的Java小知识(持续更新)

作者: LongSh1z | 来源:发表于2019-07-07 10:47 被阅读0次

本文主要是收录一些人们可能经常会忽略,但又挺重要的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线程
  • 匿名内部类
图片.png

(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的范围是从-21474836482147483647(10位),即2的31次方,而手机号码为11位

相关文章

网友评论

      本文标题:那些你所不知道的Java小知识(持续更新)

      本文链接:https://www.haomeiwen.com/subject/nltphctx.html