学习笔记之Java

基本数据类型

  java中有八种基本数据类型

    int(4字节),short(2字节),long(8字节),byte(1字节),float(4字节),double(8字节),char(2字节),boolean(1字节)。

  三种引用数据类型

    类(class),接口(interface),数组。

java命名规则

首字母是英文字母、$和下划线,由字母、数字和下划线组成。

关于int

java中整数的二进制用补码表示,正数的补码是它本身,负数的补码除符号位外按位取反加1。

JVM中一个字节以下的整型数据(int,long,short)会在JVM启动的时候加载进内存,除非用new Integer()显式的创建对象,否则都是同一个对象。
自动装箱时,byte数据的包装类对象会直接从cache中获取。
public static Integer valueOf(int i) {
        //判断i是否在-128和127之间,存在则从IntegerCache中获取包装类的实例,否则new一个新实例
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

关于char

  java采用Unicode编码,每个字符占两个字节。

关于float和double

  float经度是小数点后7到8位,double经度小数点后15到16位。
  在java中不要用f1==f2来判断两个浮点数相等,因为精度影响可能得出错误的结果,最好用f1-f2==0来判断。
  float或double进行加减运算的偏差:

0.0+5.1 = 5.1 正确
0.1+5.1 = 5.199999999999 错误 

  在Java中,double值是IEEE浮点数。除非它们是2的幂(或2的幂的和,例如1/8 + 1/4 = 3/8),否则即使它们具有高精度,也不能精确地表示它们。用Math类中的方法可以避免这种错误。

关于byte的加减法

  byte在做加减运算时会自动转成int类型,因此两个byte变量加减得到的是int值,除非byte变量被final修饰。

类型自动转换优先顺序

  转换原则:从低精度向高精度转换byte 、short、int、long、float、double、char
  两个char型运算时,自动转换为int型;当char与别的类型运算时,也会先自动转换为int型的,再做其它类型的自动转换


运算符

  算数运算符:

单目:~(按位取反)、! (取非)、-(负号运算符)、 ++(自增)、 - -(自减)、
双目:+ - * / %(取余)
三目:a>b?true:false 说明:当a大于b的时候,为true(也就是冒号之前的值),否则为false;这整个运算符包括一个关系运算符(可以是“>”"<""!="等等),一个“?”,一个“:”,冒号前后需要有两个表达式或者是值或者是对象。

  关系运算符:

等于符号:==,不等于符号:!= ,大于符号:>, 小于符号:<,大于等于符号:>= ,小于等于符号:<= 。

  逻辑运算符:

与(&&)、非(!)、或(||)

  位运算符 

与(&)、非(~)、或(|)、异或(^)

&:双目运算符,运算时均把运算数转换为二进制再做比较,规则:当相同的位上均为1时结果为1,否则结 果为0.如:1010&1101,转为二进制:1111110010&10001001101,比较结果为:1000000转为十进制: 64。所以1010&1101=64;
| :当两边操作数的位有一边为1时,结果为1,否则为0。如1100|1010=1110
~:0变1,1变0
^:两边的位不同时,结果为1,否则为0.如1100^1010=0110

  位移运算符(详情):

<< 带符号左移 >>带符号右移 >>> 无符号右移

保留字和关键字

  关键字和保留字的含义

关键字:Java的关键字对java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等。

保留字:为java预留的关键字。现在还没用到,但是在升级版本中可能作为关键字。

  关键字:

abstract, assert,boolean, break, byte, case, catch, char, class, const, continue, default, do, double, else, enum,extends, final, finally, float, for, if, implements, import, instanceof, int, interface, long, native, new,package, private, protected, public, return, short,static, strictfp, super,switch, synchronized,this, throw, throws, transient,try, void, volatile, while 

  保留字:

byValue, cast, false, future, generic, inner, operator, outer, rest, true, var , goto ,const,null

封装,继承和多态

  封装:

作用域             当前类           同一package            子孙类             其他package 

public              √                 √                     √                    √ 

protected           √                 √                     √                    × 

friendly            √                 √                     ×                    × 

private             √                 ×                     ×                    × 

  对于外部类来说,修饰符只能用public和默认。因为private作用域是当前类,对外部类没有意义,protected作用域子类,对当前类也没有意义。

  继承和多态自由发挥
  方法重写和方法重载:

方法重写:重写的方法从父类继承,必须有相同的方法名,返回值和参数列表。访问权限必须大于等于被重写的方法。
方法重载:重载发生在同一个类中,重载方法与被重载方法的方法名必须相同,参数列表必须不同(可以是个数,或者类型,其一成立即可)。访问权限和返回值类型不影响重载。
静态分派和动态分派

  一个变量例如 Human hu=new man();其静态类型为Human,动态类型为man。java在编译时并不知道hu具体是什么类型,只能知道其静态类型即Human。
  方法重载的参数类型是基于静态类型来判断的,如下代码:

public class StaticDispatch {  
    static abstract class Human{  
    }  
    static class Man extends Human{  
    }  
    static class Woman extends Human{  
    }  
    public static void sayHello(Human guy){  
        System.out.println("hello,guy!");  
    }  
    public static void sayHello(Man guy){  
        System.out.println("hello,gentlemen!");  
    }  
    public static void sayHello(Woman guy){  
        System.out.println("hello,lady!");  
    }  

    public static void main(String[] args) {  
        Human man=new Man();  
        Human woman=new Woman();  
        sayHello(man);  
        sayHello(woman);  
    }  
}  
输出:
hello,guy!
hello,guy!

  动态分派参见多态,即对象调用方法时根据多态决定调用父类还是子类方法。


类和对象相关

类的初始化过程
1. 初始化父类中的静态成员变量和静态代码块 ; 
2. 初始化子类中的静态成员变量和静态代码块 ; 
3.初始化父类的普通成员变量和代码块,再执行父类的构造方法;
4.初始化子类的普通成员变量和代码块,再执行子类的构造方法; 
null

  null可以被强制类型转换成任意类型。
  null可以理解成一个不存在的对象,可以用它来调用方法,只不过会出现空指针异常。
  综上以下代码成立:

public class TestClass {
   private static void testMethod(){
        System.out.println("testMethod");
   }
   public static void main(String[] args) {
        ((TestClass)null).testMethod();
   }
}
Object类

  Object类中涉及的方法有

public final native Class<?> getClass();//默认返回当前实际运行的类的包名+类名
public native int hashCode();
public boolean equals(Object obj) {//默认比较内存地址
        return (this == obj);
    }
protected native Object clone() throws CloneNotSupportedException;
public String toString() {//默认返回类名加哈希值。
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

public final native void notify();//
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;//
protected void finalize() throws Throwable { }//被垃圾回收调用,可以通过重写该方法在垃圾回收前做一些事情。

内部类
public class Enclosingone {
    //非静态内部类
    public class InsideOne {}
    //静态内部类
    public static class InsideTwo{}
}

class Mytest02{
    public static void main(String args []){
        Enclosingone.InsideOne obj1 = new Enclosingone().new InsideOne();//非静态内部类对象
        Enclosingone.InsideTwo obj2 = new Enclosingone.InsideTwo();//静态内部类对象
    }
}
1.外部类不能直接访问内部类,必须通过Outer.Inner来访问(如果import了Outer.Inner则可以直接访问)。
2.非静态内部类生成对象时需要外部类对象如上代码。
3。静态内部类可以直接生成对象如上代码。
匿名内部类

  匿名内部类不能定义构造器,因为构造器需要以类名来命名。


JVM相关

  这篇文章写的不错。

  JVM运行时数据区如下图:
                jvm运行时数据区结构

方法区:

  主要存放类信息,静态变量和常量。方法区是线程共享的(不同线程可以通过类名直接访问同一个常量)。由于反射机制,jvm很难预测哪个类之后不再使用,因此针对方法区的垃圾回收主要是对常量池的回收。java1.7之后常量池已经被移到堆内存中去了。方法区在达到其容量上限后会抛出OutOfMemoryError。

虚拟机栈:

  虚拟机栈是线程私有区域。其生命周期和其所在线程相同。虚拟机栈的基本单位是栈帧,栈帧中存放了该方法对应的上下文(局部变量表,出口等)。当执行引擎调用一个方法时,会为该方法创建一个栈帧并入栈,当方法执行完毕,则栈帧出栈,传送门
  虚拟机栈可能抛出两种异常。StackOverFlowError和OutOfMemoryError。前者是同一个栈中的方法数过多导致,后者是同时存在的栈过多导致。
  栈帧(Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接 (Dynamic Linking)、方法返回值和异常分派(Dispatch Exception)。

栈帧中包含:局部变量表、操作数栈、动态链接、方法返回地址、附加信息。

  编译程序代码的时候,就已经确定了局部变量表和操作数栈的大小,而且在方法表的Code属性中写好了。不会受到运行期数据的影响。
  局部变量表是一片逻辑连续的内存空间,最小单位是Slot,用来存放方法参数和方法内部定义的局部变量。我觉得可以想成Slot数组。虚拟机没有明确指明一个Slot的内存空间大小。但是boolean、byte、char、short、int、float、reference、returnAddress类型的数据都可以用32位空间或更小的内存来存放。这些类型占用一个Slot。Java中的long和double类型是64位,占用两个Slot。
  局部变量表是有索引的,就像数组一样。从0开始,到表的最大索引,也就是Slot的数量-1。要注意的是,方法参数的个数 + 局部变量的个数 ≠ Slot的数量。因为Slot的空间是可以复用的,当pc计数器的值已经超出了某个变量的作用域时,下一个变量不必使用新的Slot空间,可以去覆盖前面那个空间。

在一个实例方法的调用时,局部变量表的第0位是一个指向当前对象的引用,也就是Java里的this。

  

本地方法栈:

  本地方法栈和虚拟机栈类似,同样是线程私有,不同的是本地方法栈为Native方法服务。本地方法栈也会抛出StackOverFlowError和OutOfMemoryError。

堆:

  堆是虚拟机中最大的一块内存,被所有线程共享,几乎所有的对象都分配在堆内存中。堆是垃圾收集器主要管理的区域。GC机制会在后面介绍。

程序计数器:

  程序计数器是线程私有的,用来记录当前线程执行到哪个指令。占用空间小,可以看做是当前线程执行字节码的行号指示器。如果线程在执行Java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址;如果执行的是Native方法,这个计数器的值为空(Undefined)。此内存区域是java虚拟机规范唯一没有规定OutOfMemoryError情况的区域。传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值