java八大数据类型(各种编程语言给数据类型分配的内存大小,即位数,如果溢出则会不准):
自动拆箱:计算数值时,integer会自动转为int进行计算
自动装箱:int传入integer引用时,数值会包装为integer
int自动拆装箱只在-128~127范围中进行,超过该范围的两个integer的 == 判断返回false
编译器进行自动拆箱装箱,虚拟机运行的时候压根不知道这个。编译过程我们看不到
注意点:
方法中的基本类型:存在虚拟机栈的局部变量表里,变量是一个引用,指向局部变量表里的值
类的基本类型成员变量:由于对象存在堆中,所以成员变量也在堆中,变量存的就是这个值
包装类对象:eg string.intern,会先在常量池找是否有该对象,有的话直接返回地址,即让引用指向常量对象的内存地址,Integer类中valueOf方法相同。所以基本数据类型的包装类型可以在常量池查找对应值的对象,找不到就会自动在常量池创建该值的对象。
基本类型的变量和对象的引用变量在函数的栈内存分配,new出来的则在堆。
声明基本类型时会先去栈中查找,new则不会先去堆里找,eg:
eg:int a=1;int b=1;a==b//true
Integer a = new Integer(1);Integer b = new Interger(1); a== b//false
Integer a = 1;Integer b = 1; a==b//true
jdk1.7之后的常量池被放在了堆空间。
相同点:intern方法先去查询常量池,如果找到即返回
不同点:若在常量池未找到,则不会copy到常量池,而是在常量池里生成一个对原字符串的引用
String s = "1";
先去字符串常量池检查,如果不存在,则在常量池开辟内存存放“1”;如果存在,则在栈中开辟空间,引用存放常量池中值的内存地址。
String s = new String("1");
先去字符串常量池检查,若不存在,则在常量池中开辟内存空间存放;如果存在,则不重新开辟空间,然后在堆中开辟空间存放new出的String对象,在栈中开辟空间,存放堆中String的内存地址,引用指向new出的对象。
常量池推荐大佬博客
表达式只有常量时,编译器完成计算;表达式有变量时,运行期才计算,所以地址不一样。eg~
intern():若常量池内有,则返回常量池内的地址;若常量池内没有,则1.7之后返回类对象的引用地址。
equals():比较字符串内容是否一致。
底层继承父类的可变字符数组value,初始化容量为16,底层都是利用可修改的char数组(JDK 9 以后是 byte数组)。
除了hash这个属性其它属性都声明为final,因为它的不可变性,所以例如拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。
StringBuffer在大部分涉及字符串修改的操作上加了锁,保证线程安全,效率较低。
★所以如果我们有大量的字符串拼接,如果能预知大小的话最好在new StringBuffer 或者StringBuilder 的时候设置好capacity,避免多次扩容的开销。扩容要抛弃原有数组,还要进行数组拷贝创建新的数组。
String在用+运算符时,先封装成StringBuilder,调用append,再toString返回,所以大量使用+操作时,应用StringBuilder代替String。
扩容:append方法调用ensureCapacityInternal,计算空间是否足够,不够则扩容,扩容长度为原来的两倍+2,若扩容后长度超过jvm支持的最大数组长度,则有两种情况。
删除:实际上是将剩余的字符重新拷贝到字符数组
常见场景:
首先final修饰的类只保证不能被继承,并且该类的对象在堆内存中的地址不会被改变。
但是持有String对象的引用本身是可以改变的,比如他可以指向其他的对象。
final修饰的char数组保证了char数组的引用不可变。但是可以通过char[0] = ‘a’来修改值。不过String内部并不提供方法来完成这一操作,所以String的不可变也是基于代码封装和访问控制的。
是一个常量,所以可以共享使用,节省内容。
==是运算符,equals是方法。
基本类型比较:==比较两个 值是否相等,equals不能直接用于基本类型的比较,需要进行包装。
引用对象比较:==和equal都是比较栈内存的地址是否相等,我们可以根据自己的情况重写equal的方法,一般自动生成,去比较两个成员变量值是否相同。(String比较特殊)
equals相同的两个对象,hash码也应当相同!!!
与finally和finalize的区别:
用于保证程序最基本、关键的正确性;检查通常在开发和测试时开启。为了提高性能,在软件发布后, assertion 检查通常是关闭的。
异常:Exception以及他的子类,代表程序运行时发生的各种不期望发生的事件,可以被java异常处理机制使用,是异常处理的核心;
非检查异常,运行期异常:
检查异常,编译器异常:
检查和非检查是对于javac来说
错误:Error类以及他的子类实例,代表JVM错误(创建特别大的内存)。必须修改代码,要不永远错误
为什么?
在一些大型的,模块化的软件开发中,一旦一个地方发生异常,则如骨牌效应一样,将导致一连串的异常。假设B模块完成自己的逻辑需要调用A模块的方法,如果A模块发生异常,则B也将不能完成而发生异常。
但是B在抛出异常时,会将A的异常信息掩盖掉,这将使得异常的根源信息丢失。异常的链化可以将多个模块的异常串联起来,使得异常信息不会丢失。
异常链化!
以一个异常对象为参数构造新的异常对象。新的异对象将包含先前异常的信息。这项技术主要是异常类的一个带Throwable参数的函数来实现的。这个当做参数的异常,我们叫他根源异常(cause)。
查看Throwable类源码,可以发现里面有一个Throwable字段cause,就是它保存了构造时传递的根源异常参数。这种设计和链表的结点类设计如出一辙,因此形成链也是自然的了。eg~
public class Throwable implements Serializable {
private Throwable cause = this;
public Throwable(String message, Throwable cause) {
fillInStackTrace();
detailMessage = message;
this.cause = cause;
}
public Throwable(Throwable cause) {
fillInStackTrace();
detailMessage = (cause==null ? null : cause.toString());
this.cause = cause;
}
//........
}
如果要自定义异常类,则扩展Exception类即可,因此这样的自定义异常都属于检查异常(checked exception)。如果要自定义非检查异常,则扩展自RuntimeException。
try…catch…finally中的return 只要能执行,就都执行了,他们共同向同一个内存地址(假设地址是0×80)写入返回值,后执行的将覆盖先执行的数据,而真正被调用者取的返回值就是最后一次写入的。
1.当子类重写父类的带有 throws声明的函数时,其throws声明的异常必须在父类异常的可控范围内——用于处理父类的throws方法的异常处理器,必须也适用于子类的这个带throws方法 。这是为了支持多态。
例如,父类方法throws 的是2个异常,子类就不能throws 3个及以上的异常。父类throws IOException,子类就必须throws IOException或者IOException的子类。
2.Java程序可以是多线程的。每一个线程都是一个独立的执行流,独立的函数调用栈。如果程序只有一个线程,那么没有被任何代码处理的异常 会导致程序终止。如果是多线程的,那么没有被任何代码处理的异常仅仅会导致异常所在的线程结束。
也就是说,Java中的异常是线程独立的,线程的问题应该由线程自己来解决,而不要委托到外部,也不会直接影响到其它线程的执行。
成员内部类是最普通的内部类,它的定义为位于另一个类的内部。
使用:
间接使用:在外部类方法当中,使用内部类
直接:外部类名.内部名称。。。。。
内部类成员变量:this.xxx ;外部类成员变量:外部类.this.xxx
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。因为属于一个变量,所以不能用public等修饰。(包含匿名内部类)。
重点:局部内部类访问方法的局部变量,在1.7之前是需要讲局部变量进行final修饰;为什么?因为局部变量在java栈的变量表里面,而new出来的方法是在堆内存,方法结束局部变量消失,而堆内存一直存在,所以要留一个变量值;
加static的,不用依赖外部类,不能访问非static属性。
匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
使用情况:如果接口的实现类就实现一次,那么可以省略这个实现类,直接用匿名内部类
内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号
内部类不能用普通的方式访问。如果存在和外部类相同的属性,发生隐藏情况,需要外部类.this成员。
静态内部类不能随便的访问外部类的成员变量,只能访问外部类的静态成员变量。
外部类不能直接访问内部类的的成员,但可以通过内部类对象来访问,必须先创建一个成员内部类的对象,通过指向这个对象的引用来访问。
内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的。
泛型,即“参数化类型”,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
是一种未知得类型,当我们不知道使用什么数据类型得时候,可以使用泛型。
反编译出现泛型信息。
大佬博客~
编译的特性:
大佬博客!
一般会实现一部分操作,并且留一部分抽象方法让子类自己实现
一般指一种规定,如果想实现一个具体的接口,接口中的方法就必须按照规定去实现,接口可以继承多个其他接口,但类智能单继承。
java8开始接口中的方法允许有方法体(静态方法和默认方法),java9可以使用私有静态方法。
注意事项:
用于访问权限控制,可以保护类中的信息,只提供想要被外界访问的信息。
访问范围 public>protected>package=default>private。
大佬博客!
java只允许单继承,但可以通过内部类继承其他类来实现多继承
程序在运行的过程中,同一种类型在不同的条件下表现不同的结果。多态也称为动态绑定,一般是在运行时刻才能确定方法的具体执行对象,这个过程也称为动态委派。顾名思义:一个对象拥有多种形态;
一般可分为两种,一个是重写overwrite,一个是重载override!
由于继承关系中的子类有一个和父类同名同参数的方法,会覆盖掉父类方法。
又叫运行时多态,编译时看不出子类调用哪个方法,运行时操作数栈会先根据子类引用去子类的类信息中查找方法,找不到的话再到父类信息中查找方法。
一个同名方法可以传入多个参数组合,同名方法如果参数相同,即使返回值不同也不能同时存在,编译会出错。
又叫编译时多态,编译期可以确定传入的参数组合,决定调用的具体方法是哪一个。
JAVA基础就总结到这里了,后面会继续完善JAVA反射机制和JAVA集合的知识!如大家喜欢可以收藏~
Java反射机制传送~
Java集合知识点总结传送~