Java笔记之基本概念

本笔记来自 计算机程序的思维逻辑 系列文章

整数

不同类型的大小

类型 大小(bit)
byte 8
short 16
int 32
long 64

二进制表示

  • 最左边一位是符号位,0 表示正数,1 表示负数
  • 正数对应的负数由其补码表示,即取反再加 1
  • 从负数二进制表示,依然是通过取反再加1得到二进制,继而求得十进制值

位运算

  • 左移 << :高位舍弃,低位补 0
  • 有符号右移 >>:右边舍弃,左边补什么取决于原来高位是什么
  • 无符号右移 >>>:右边舍弃,左边补 0
  • 按位与 &:两边同时为 1 才为 1
  • 按位或 |:一边为 1 则为 1
  • 按位取反 ~1001
  • 按位异或 ^:相异为 1,相同为 0

浮点数

  • float32 位,1位表示符号,23位表示尾数,8位表示指数
  • double64 位,1位表示符号,52位表示尾数,11位表示指数

编码

  • ASCII:美国信息互换标准代码,1个字节表示,128 个字符:0~127
  • ISO 8859-1:西欧国家标准,128~159控制字符160~255西欧字符
  • Windows-1252:西欧国家标准,丰富并取代了ISO 8859-1
  • GB2312:简体中文,2个字节表示,最高位都是1;约 7000 个汉字
  • GBK:在GB2312基础上增加 14000+ 个汉字,包括 繁体字
  • GB18030:在GBK基础上增加 55000+ 个汉字,包括少数民族字符和中日韩统一字符;使用 变长 编码,有的2个字节,有的4个字节
  • Big5:针对繁体中文,两个字节表示,包括 13000+ 个繁体字
  • Unicode:给 所有 字符分配了一个唯一的数字编号,范围110万
    • UTF-8:使用变长字节,1个到4个不等;小于128的,最高位为0,编码和 ASCII 一样;其他的,第一个字节最高位有几个1,表示由几个字节组成,其他字节都以10开头
    • UTF-16:使用变长字节,有的2个,有的4个
    • UTF-32:4个字节表示
      • UTF-32BE:第一个字节是整数二进制的最高位,最后一个字节是整数二进制的最低位 (Big Endian)
      • UTF-32LE:与上相反

对于一个Unicode编号,具体怎么编码呢?首先将其看做整数,转化为二进制形式(去掉高位的0),然后将二进制位从右向左依次填入到对应的二进制格式x中,填完后,如果对应的二进制格式还有没填的x,则设为0。

条件的本质

if else

  • 会转换为跳转指令被CPU识别
  • 跳转有两种:条件跳转和无条件跳转

switch

  • if else不一样,如果分支较多,可能会使用 跳转表
  • 跳转表 是一个映射表,存储可能的值以及要跳转的地址
  • 跳转表 的高效体现在 值必须是整数,且按大小排序
  • switch的值可以为byte short int char enum String;跳转表值的存储空间为32位,放不下long

字符 char

  • 本质是一个固定占用 两个字节 正整数
  • 这个正整数对应于Unicode编号,用于表示对应的字符
  • 由于固定占用两个字节,只能表示 65536 以内的字符,超出范围的字符,使用两个char表示

函数

  • 函数中的参数和函数内定义的变量,都分配在栈中,这些变量只在函数被调用时才分配,调用结束就被释放;返回值存放在专门的返回值存储器中
  • 对象的变量存储在栈中,内容存储在堆中
  • 函数的每一次调用都需要分配额外的栈空间用于存储参数,局部变量以及返回地址,需要进行额外的入栈和出栈操作

继承和多态

类的继承

  • new过程中,父类先进行初始化,可通过super调用父类相应的构造方法,没有使用super的话,调用父类的默认构造方法
  • 子类变量和方法与父类重名的情况下,可通过super强制访问父类的变量和方法
  • 父类没有不带参数的构造方法,则子类在构造方法中必须通过super(…)调用父类的带参数的构造方法

类的多态

子类对象可以赋值给父类引用变量,这叫多态,实际执行调用的是子类实现,这叫动态绑定

静态绑定

即访问绑定到变量的静态类型

  • 静态绑定在程序编译阶段即可决定,而动态绑定则要到程序运行时
  • 实例变量、静态变量、静态方法、私有方法,都是静态绑定

重载

当有多个重名方法时,在决定调用哪个方法的过程中,首先寻找参数类型最匹配的,然后才看变量的动态类型,进行动态绑定

类型转换

一个父类的变量,能不能转换为一个子类的变量,取决于这个父类变量的动态类型(即引用的对象类型)是不是这个子类或子类的子类

类的加载

分配内存保存类的信息,给类变量赋默认值,加载父类,设置父子关系,执行类初始化代码

虚方法表

类加载的时候,为每个类创建一个表,这个表包括该类的对象所有动态绑定的方法及地址,包括父类的方法,但一个方法只有一条记录,子类重写父类的方法后只保留子类的

避免使用继承

  • 使用final关键字
  • 优先使用 组合 而非继承
  • 使用 接口

正确使用继承

  • 重写方法不要改变预期的行为
  • 理解可重写方法的实现机制
  • 基类修改时,相应修改子类

接口

  • 接口表示能力
  • 降低耦合,提高灵活性
  • 接口不能new,不能直接创建对象,只能通过类来创建对象;可以声明接口类型的变量,引用实现了该接口的类对象
  • 接口中的变量都是public static final
  • 接口可以多继承

抽象类

  • 表达一种抽象的概念
  • 定义了抽象方法的类必须被声明为抽象类,但抽象类可以没有抽象方法
  • 抽象类不能创建对象,但可以声明抽象类的变量,引用抽象类具体子类的对象,与接口类似
  • 从语法看,抽象类不是必须的,但它能使程序更为清晰,减少误用

内部类

定义在类的内部,可以实现对外部隐藏,可以有更好的封装性,代码也更简洁

静态内部类

  • 可以访问外部类的静态变量和静态方法,而不能访问实例变量和实例方法
  • 在外部类内部,可以直接使用该内部类

成员内部类

  • 不仅可以访问外部类的静态变量和静态方法,还可以访问实例变量和实例方法
  • 在外部类内部,同样可以直接使用该内部类
  • 在成员内部类中,不可用定义静态变量和静态方法

方法内部类

  • 如果是静态方法,该内部类只能访问外部类的静态变量和静态方法
  • 如果是实例方法,则还可以直接访问外部类的实例变量和实例方法
  • 方法内部类可以访问方法中的参数和局部变量,是通过在构造方法中传递参数实现的
  • 如果类只在某个方法中被使用,那么使用方法内部类,可以实现更好的封装

匿名内部类

  • 在创建对象的时候定义类,与new关联
  • 只能被使用一次,用来创建一个对象
  • 因为没有构造方法,所以无法接收参数
  • 可以访问外部类的所有变量和方法,可以访问方法中的final参数和局部变量

枚举

  • 枚举变量的toString()方法返回其字面值,所有枚举类型都有一个name()方法,返回值和toString()一样
  • 枚举变量可以用equals==比较,结果是一样的
  • 枚举是有顺序的,可以比较大小;枚举类型都有一个ordinal()方法,表示枚举值在声明时的顺序,从 0 开始
  • 枚举类型都实现了Comparable接口,可以通过compareTo()方法与其他枚举值比较,实际就是比较ordinal的大小
  • 枚举类型都有一个静态的valueOf(String)方法,返回该字符串对应的枚举值
  • 枚举类型都有一个静态的values()方法,返回一个包括所有枚举值的数组,顺序和声明时一样
  • 一般不使用枚举的ordinal值,因为它会随着枚举值在定义中的位置变化而变化;通常增加一个实例变量来处理,好处是:该变量的值可以自定义,且不会改变
  • 每个枚举值可以有关联的类定义体,枚举类型中声明抽象方法,枚举值实现该方法

异常

异常分类

所有异常都继承自 Throwable

Error
  • VirtualMachineError
  • OutOfMemoryError
  • StackOverFlowError
Exception
  • IOException
  • SQLException
  • RuntimeException
    • NullPointerException
    • IllegalStateException
    • ClassCastException
    • IllegalArgumentException
    • NumberFormatException
    • IndexOutOfBoundsException
    • ArrayIndexOutOfBoundsException
    • StringIndexOutOfBoundsException

方法

  • 获取异常消息:getMessage()
  • 获取触发异常的异常:getCause()
  • 获取异常栈每一层的信息:getStackTrace()
  • 打印异常栈信息:printStackTrace()

异常的捕获

  • catch语句可以有多条,具体的异常子类应该放前面,因为抛出的异常类型如果是catch中声明的异常的子类也算匹配,后面的catch就不再执行了
  • catch块内处理完后,可以重新抛出该异常或新异常
  • finally,不管有无异常抛出,该代码块都会执行,常用于释放资源
  • 如果在trycatch内有return语句,则return语句在finally语句执行结束后才执行,但finally并不能改变其返回值
  • 如果finally内也有return语句,那么trycatch内的return会丢失,实际返回finally中的返回值;finally内的return语句还会掩盖trycatch内的异常;finally中抛出的异常也会掩盖trycatch内的异常

所以,尽量避免在finally中使用return语句或抛出异常

声明抛出的异常

  • 使用throws,跟在方法后面,可以有多个异常,用,隔开
  • 对于RuntimeException (unchecked exception),不要求使用throws进行声明;但对于checked exception,必须声明,没有声明则不能抛出,但可以声明而不抛出,留给子类去处理

包装类

装箱及拆箱

Java 1.5以后引入自动装箱拆箱技术

  • 装箱:基本类型->包装类型
  • 拆箱:包装类型->基本类型

装拆箱 方法

  • 接收基本类型,返回包装类型:valueOf()
  • 返回对应的基本类型:xxxValue()

equals

  • 默认实现是比较地址
  • 所有包装类都重写了该方法,实际比较用的是其包装的基本类型值
    • long比较对应的longValue
    • Float比较对应的floatToIntBits,把二进制看作int,再比较
    • Double比较对应的doubleToLongBits

hashCode

  • 返回一个对象的哈希值,int类型,由对象中一般不变的属性映射得来,用于快速对对象进行区分,分组
  • 一个对象的哈希值不能变,相同对象的哈希值必须一样
  • 如果equals方法返回true,则hashCode必须一样,反之不要求
  • 子类重写equals方法时,必须重写hashCode方法

Comparable

每个包装类都实现了Comparable接口,用于比较

字符串 String

  • 内部用一个 字符数组 表示字符串
  • String是不可变类,声明为final,内部字符数组也是final
  • 很多看似修改的方法,都是创建新的String对象实现的
  • 常量字符串在内存中,被放在一个共享的地方:字符串常量池

字符串构造器 StringBuilder

  • 内部维护一个 可变的 字符数组和一个记录使用的字符个数的实例变量
  • 字符数组默认长度为 16
  • 长度扩展策略:length * 2 + 2
  • 指数扩展,减少内存分配的次数,也避免空间浪费
  • 内部实现方法:System.arraycopy(src, srcPos, dest, destPos, length)

数组操作工具类 Arrays

  • 打印:toString(objArray)
  • 排序:sort(objArray)
  • 排序,自定义比较器:sort(objArray, Comparator)
  • 二分查找:binarySearch(objArray, obj),找到返回index,找不到返回-(插入点+1)
  • 拷贝:copyOf(objArray, newLength) copyOfRange(objArray, from, to)
  • 比较:equals(a1, a2),数组长度相同,每个元素都相同,才返回true
  • 填充:fill(objArray, obj) fill(objArray, from, to, obj)

时间和日期

纪元时

1970年1月1日0时0分0秒

Date

  • 绝对时间,和年月日无关
  • 内部维护一个long类型的值,毫秒值

Calendar

  • 年历,抽象类
  • 内部有一个表示时刻的实例变量time,还有一个整型数组fields,表示日历中各个字段的值,数组长度为 17
  • 内部字段,静态变量
    • YEAR
    • MONTH
    • DAY_OF_MONTH
    • DAY_OF_WEEK
    • HOUR_OF_DAY
    • MINUTE
    • SECOND
    • MILLISECOND
  • 方法
    • add,会自动调整其他字段的值
    • roll,不影响时间范围更大的字段值
  • 公历:GregorianCalendar

DateFormat

  • 格式化,抽象类
  • 定义了4个静态变量,表示4种风格,默认是MEDIUMSHORT MEDIUM LONG FULL
  • SimpleDateFormat,格式:
    • yyyy:年
    • MM:月
    • dd:日
    • HH:时
    • mm:分
    • ss:秒
    • SSS:毫秒
    • E:星期几
    • a:上下午
  • 时区:Timezone
  • 国家和语言:Locale

随机 Random

  • 种子 种子决定了随机产生的序列,种子相同,产生的随机数序列就是相同的;指定种子就是为了实现可重复的随机
  • 默认构造方法的种子是一个真正的随机数

泛型

Java有Java编译器和Java虚拟机,Java编译器将Java源代码编译成.class文件,虚拟机加载并运行.class文件

对于泛型类,Java编译器会将泛型代码转换为普通的非泛型代码,将类型参数T擦除,替换成Object,插入必要的强制类型转换

好处

安全性和可读性

通配符 ?

  • :有限定通配符,匹配E或E的某个子类(只能读,不能写)
  • :超类型通配符,表示E的父类(能写入)
  • :无限定通配符(只能读,不能写)

比较

  • :用于定义类型参数,它声明了一个类型参数T,可放在泛型类定义中类名后面,泛型方法返回值前面
  • :用于实例化泛型变量中的类型参数

总结

  • 用于实现更灵活的读取,可以用类型参数的形式替代,但通配符更为简洁
  • 用于实现更为灵活的写入和比较,不能被类型参数形式替代

注意

  • 基本类型不能实例化类型参数
  • 一个泛型对象的getClass()方法的返回值与原始类型是相同的
  • 不能通过类型参数创建对象
  • 多个上界的类型参数,用&分隔,上界类写在第一位,上界接口写在后面
  • 不能创建泛型数组,但可以使用原始类型来创建

你可能感兴趣的:(Java笔记之基本概念)