美文网首页
笔记(一 )——Java零碎知识摘录

笔记(一 )——Java零碎知识摘录

作者: 木溪bo | 来源:发表于2019-10-11 17:06 被阅读0次

    ——》个人平时笔记,看到的同学欢迎指正错误,文中多处摘录于各大博主精华、书籍

    1、android:exported属性:主要作用是:是否支持其它应用调用当前组件。默认值:如果包含有intent-filter 默认值为true; 没有intent-filter则默认值为false。虽然说加了这个属性,可以使该组件不被其他的程序调用。但是,需要注意的是,如果两个程序的userid是一样的话,exported=”false”会失效。

    2、 java反射:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。即:java在运行状态中反射知道一个类里面的所有方法和属性;通过反射获得对象后,能够调用对象的任意属性和方法。

    java内省:内省基于反射实现,主要用于操作JavaBean,通过内省 可以获取bean的getter/setter,只要看到有getter或者setter就认为这个类有那么一个字段,比如看到getName() 内省就会认为这个类中有name字段,但事实上并不一定会有name。

    image

    3、泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。参数化类型就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参),由传入的实参类型决定的参数类型。在方法中使用通配符“?”作为形参,实则它的使用是一种类型实参,此时“?”可以理解为说有类型实例的父类,就是一种真实的类型。
    java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

    public void showKeyValue1(Generic<?> obj){
     
         Log.d("泛型测试","key value is " + obj.getKey());
     
     }
    
    image
    4、 泛型通配符“?”(泛型详解)

    “ ? ”是通配符,泛指所有类型:一般用于定义一个引用变量,这么做的好处是,如下所示,定义一个sup的引用变量,就可以指向多个对象。

     SuperClass<?> sup = new SuperClass<String>("lisi");
    
           sup = new SuperClass<People>(new People());
     
           sup = new SuperClass<Animal>(new Animal());
     
     若不用?,用固定的类型的话,则:
    > 
    > SuperClass<String> sup1 = new SuperClass<String>("lisi");
    > 
    > SuperClass<People> sup2 = new SuperClass<People>("lisi");
    > 
    > SuperClass<Animal> sup3 = new SuperClass<Animal>("lisi");
    

    这就是?通配符的好处。

    ? extends T, ?是指T类型或T的子类型

    ? super T ,?是指T类型或T的父类型

    这个两个一般也是和?一样用在定义引用变量中,但是传值范围不一样

    T和?运用的地方有点不同,?是定义在引用变量上,T是类上或方法上;如果有泛型方法和非泛型方法,都满足条件,会执行非泛型方法。

    带不同泛型的ArrayList在编译后生成的Class是相同的!也就是说,泛型在编译生成字节码文件时会被”擦除”;不管ArrayList<E>带什么泛型,在编译后都是ArrayList所对应的字节码文件。

    eg:

    image

    5、Java 如何有效地避免OOM:善于利用软引用和弱引用

    强引用:直接的对象引用,实例化对象。在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。在方法内部有一个强引用,这个引用保存在 java栈 中,而真正的引用内容 (Object)保存在 java 堆中。当这个方法运行完成后,就会退出方法栈,则引用对象的引用数为 0 ,这个对象会被回收。

    软引用:有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。当一个对象只有软引用存在时,系统内存不足时此对象会被JVM回收。一般用于服务端。

    弱引用:弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。只要JVM进行垃圾回收,被弱引用关联的对象必定会被回收掉。移动端内存紧缺推荐使用弱引用。

    虚引用:虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收

    在使用软引用和弱引用的时候,我们可以显示地通过System.gc()来通知JVM进行垃圾回收,但是要注意的是,虽然发出了通知,JVM不一定会立刻执行,也就是说这句是无法确保此时JVM一定会进行垃圾回收的。

    6、try catch是直接处理,处理完成之后程序继续往下执行;throw则是将异常抛给它的上一级处理(是明确的一定会抛出异常),程序便不往下执行了。

    throws并不是抛出一个实际的Exception而是一个异常声明,它声明这个方法可能会抛出一个异常,注意是可能,所以在没有异常的情况下也是可以用throws的,而throws本身的作用也是用来提高程序的健壮性,反过来,如果这个方法的的确确的有一个异常,那么编译器会强制让你加上throws这个异常声明。

    try这个程序块名字很易懂,就是尝试,尝试能不能正常的走完整个作用域,所以我们在try块里经常放上可能会抛出异常的程序段。而catch恰好就是处理try里抛出来的异常,其中catch的参数列表接收的是一个异常的引用,是throw抛出来的异常的引用,这样我们就可以得到这个异常的对象,倒不如说我们是得到了这个异常对象的引用,因为在java里,通常我们也只能通过引用来操纵对象。

    7、一个类只能初始化一次,却可以多次实例化。java虚拟机类加载机制

    类的初始化的执行顺序

    》没有父类的情况:

    类的静态属性

    类的静态代码块

    类的非静态属性

    类的非静态代码块

    构造方法

    》有父类的情况:

    父类的静态属性

    父类的静态代码块

    子类的静态属性

    子类的静态代码块

    父类的非静态属性

    父类的非静态代码块

    父类构造方法

    子类非静态属性

    子类非静态代码块

    子类构造方法

    部分特殊不引起类初始化,先记录下吧。

    通过子类引用父类的静态字段,不会导致子类初始化,对于静态字段,只有直接定义这个字段的类才会被初始化

    通过数组定义来引用类,不会触发此类的初始化

    常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类初始化

    public static final int x =6/3; 能够在编译时期确定的,叫做编译常量,不会引起类的初始化!!!

    public static final int x =new Random().nextInt(100); 运行时才能确定下来的,叫做运行时常量,运行时常量会引起类的初始化!!!,类的初始化只会有一次

    在虚拟机规范中使用了一个很强烈的限定语:“有且仅有”,这5种场景中的行为称为对类进行主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。

    8、字符串在Java生产开发中的使用频率是非常高的,字符串对于我们而言非常关键。在C中是没有String类型的,那么C语言要想实现字符串就必须使用char数组,通过一个个的字符来组拼成字符串。而在Java中String源码分析可知,String也是char数组拼接而成的。字符串一定会被保存到常量池中,且Java虚拟机如果发现常量池中已经存在需要创建的字符串中,它就不会重复创建,而是指向那个字符串即可,(深入Java源码剖析之字符串常量)这就类似于如下操作。

     Student aa=new Student(123,"小花");//保存在堆内存中,而非常量池
     Student bb=aa;
     Student cc=aa;
    

    9、Java中的 数据类型分两种:基本数据类型和引用数据类型
    基本数据类型:Java中的简单类型(基本数据类型)从概念上分为四种:实数、整数、字符、布尔值。八种基本数据类型如下:

    实数:double、float
    整数:byte、short、int、long
    字符:char
    布尔值:boolean

    复杂类型(引用数据类型):String、类对象、数组。

    引用数据类型和基本数据类型的内存模型本质上是不一样的。
    基本数据类型的存储原理是这样的:所有的基本数据类型不存在“引用”的概念,基本数据类型都是直接存储在栈内存中的,栈空间里存储的地址就是数据本身的值。而Java语言里面只有这八种基本数据类型是这种存储模型;

    而其他的只要是继承于Object类的引用数据类型,都是按照Java里面存储对象的内存模型来进行数据存储的。引用数据类型使用Java内存堆和内存栈来进行这种类型的数据存储;堆内存存储值,栈中存储对象在堆内存的首地址(引用地址),而这个栈引用就叫引用变量。

    简单地讲,基本数据类型是存储在有序的栈内存中的,而引用数据类型本身的值存储在堆内存中的。

    基本数据类型由于长度固定,且需要空间比较少,所以直接存储在栈中;而对象比较大,所以栈中只存储一个4btye大小的引用地址,堆内存中存值。

    10、java中声明了一个变量,便会给这个变量分配内存空间,且被声明的变量会对应有一个内存地址生成。在类型转换中,低比特数(bit)的数据类型向高比特数的类型转换可以自动转换,高比特数的数据类型向低比特数的类型转换就需要使用强制类型转换了。

    比特数由低到高 byte->short->char->int->long->float->double

    1.bit就是位,也叫比特位, 也是b ,是计算机表示数据最小的单位

    2.byte就是字节,也是B

    3.1byte=8bit

    4.1byte就是1B

    5.一个字符=2字节

    6.1KB=1024B

    7.转换关系如下:

    (1)1KB=1024B

    (2) 1B= 8b

    11、栈内存、堆内存、常量池区别:

    Java内存分配主要包括以下几个区域:

    1. 寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制
    1. 栈:存放基本数据类型的数据和对象的引用(引用变量),但对象本身不存放在栈中,而是存放在堆中
    1. 堆:存放用new产生的数据或者说对象
    1. 静态域:存放在对象中用static定义的静态成员
    1. 常量池:存放常量。字符串常量(例如:String a="123")和final修饰的基本数据类型常量。
    1. 非RAM(随机存取存储器)存储:硬盘等永久存储空间
    字符串
     由上图可以看出,使用双引号直接定义的String对象会指向常量池中的同一个对象,通过new产生一个字符串(假设为“china”)时,会先去常量池中查找是否已经有了“china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象

    ----》好文参考推荐:Java内存分配之堆、栈和常量池

    修饰符作用域
    12、静态变量、静态方法是不属于某个实例对象,而是属于类的,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。每个静态变量内存地址是唯一的,UserBean类中有一个name静态变量,不管new UserBean()多少个实例,name变量所指向的内存地址是唯一的(也就是说m1.name、m2.name和UserBean.name指向同一个变量),只要有一个地方对静态变量做更改,所有使用引用的地方都会改变,eg:m1.name=“小红”,则m2.name、UserBean.name都是改为了“小红”。

    静态初始化器:静态初始化器是类中由static修饰的一对“{ }”包裹的,不同于构造方法,它并不是方法,因为它并没有返回值和方法名。它是属于类的,在初始化器加载类的时候初始化一次,其它时候不执行不加载。多个静态初始化器是按编码秩序依次执行。

    注意:静态内部类默认持有外部类实例引用, 而导致外部类无法释放,最终造成内存泄露。这时候就要给外部实例引用添加弱引用标识符了,尽可能的避免泄漏。

    是否可以从一个static方法内部发出对非static方法的调用?

    不可以。因为非static方法要与对象关联在一起,必须创建一个对象后,才可以在该对象上进行方法的调用,而static方法调用时不需要创建对象,可以直接调用。也就是说,一个static方法被调用时,可能还没有创建任何实例对象,所以,一个static方法内部不可以发出对非static方法的调用。

    13、Java中是值传递,而不是引用传递,不管是是基本数据类型还是引用数据类型都是值传递。
    java参数传递(到底是值传递还是引用传递?)
    java是值传递还是引用传递?

    14、List 接口实现了 Collection 接口,它主要有两个实现类:ArrayList 类和 LinkedList 类。

    • LinkedList是链接列表实现的,同样也是属于线程不安全,每一个值都会有两个引用,一个引用指向列表前一个值,另一个引用指向后一个值,所以它占用的内存更多。但是它对数据的添加、插入、删除是最快的。
    • ArrayList底层是基于动态数组实现的,属于线程不安全的,每一次值的增删都要要移动数组重新排列和计算数组的容量,造成速度很慢,ArrayList 中不断添加元素,其容量也自动增长。它的查找速度是最快的。
    • Vector和ArrayList基本相似,利用数组及扩容实现List,但Vector是一种线程安全的List结构,它的读写效率不如ArrayList,其原因是在该实现类内在方法上加上了同步关键字synchronized
    • Stack继承于Vector,基本特性与Vector一致,线程安全,但效率低,实际就是利用Vector抽象成栈,使之拥有后进先出的特性。

    List:有序集合
    Set:不重复集合,LinkedHashSet按照插入排序,SortedSet可排序,HashSet无序
    Map:键值对集合

    • List和Set是存储单列数据的集合,Map是存储键值对这样的双列数据的集合;
    • List中存储的数据是有顺序的,并且值允许重复;Map中存储的数据是无序的,它的键是不允许重复的,但是值是允许重复的;Set中存储的数据是无顺序的,并且不允许重复,但元素在集合中的位置是由元素的hashcode决定,即位置是固定的(Set集合是根据hashcode来进行数据存储的,所以位置是固定的,但是这个位置不是用户可以控制的,所以对于用户来说set中的元素还是无序的)。
      三大集合:List、Map、Set的区别与联系

    15、Map的几种类型

    • HashMap是基于哈希表实现的,是无序的。每一个元素是一个key-value对,其内部通过单链表解决冲突问题,HasnMap内部有自己的扩容机制,容量不足(超过了阀值)时,同样会自动增长。HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的ConcurrentHashMap。HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。HashMap中key和value都允许为null。key为null的键值对永远都放在以table[0]为头结点的链表中。

    HashMap可以使用可变对象作为key,但是对象实体类需要重写equals(Object obj)、hashCode()两方法

    • HashTable同HashMap一样是基于哈希表实现的,是无序的。同样每个元素也是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。HashTable也是JDK1.0引入的类,但是是线程安全的,适合用于多线程环境中。HashTable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。 HashTable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。现在已经过时了。
    • LinkedHashMap是HashMap的子类,是有序的集合。LinkedHashMap可以认为是HashMap+LinkedList,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序。LinkedHashMap虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序。该迭代顺序可以是插入顺序或者是访问顺序,即插入的顺序和取出的顺序相同。
    • TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。默认是按key升值排序。
    • TreeMap和LinkedHashMap是有序的,HashMap、ConcurrentHashMap、HashTable是无序的,且只有ConcurrentHashMap和HashTable是线程安全的。
      HashTable和HashMap的区别详解
    map.jpeg

    16、String,StringBuffer与StringBuilder的区别

    • String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间。
    • 和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
    • StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问),StringBuffer的append、insert等操作方法都是加synchronized锁的。但是StringBuilder 相较于 StringBuffer 有速度优势。

    17、Java中类、抽象类、接口:

    • 类:在java中所有的类的都是直接或者间接的继承Object这个类,也就是说所有的类都是Object的子类,而一般我们没有特别extends继承其他类,那我们创建的这个类的直接父类就是Object
    • 抽象类:抽象类类似于一个给其他类的一个模板,抽象类里面定义多个抽象方法,用abstract声明,供它的子类去实现这个方法(这里所谓实现就是指具体重写),在子类方法里去实现想要的具体操作。抽象类只能被子类继承去实现具体方法,而不能创建对象,即不能通过new来生成抽象类对象。子类继承了抽象类就必须要实现它的所有抽象方法。
    • 接口:接口可以说是特殊的抽象类,因为接口类里面只能只能含abstract声明的抽象方法存在,一般abstract是默认存在不需要我们写出来的。接口的属性成员必须初始化。接口只能被子类implements实现,而不能直接extends继承,但是接口类A可以" A implements B , C "其中A、B、C都是接口,即接口具有多继承性。

    相关文章

      网友评论

          本文标题:笔记(一 )——Java零碎知识摘录

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