java笔记

看Java编程思想做的一点笔记,有点乱,但也能看,,,

异常

throws

考虑如下情况:
主方法调用method1, method1调用method2, method2中打开文件

  • method2中需要进行异常处理,但是method2不打算处理,而是把这个异常通过throws 抛出去。在方法声明中使用throws处理异常。
  • 那么method1就会接到该异常。 处理办法也是两种,要么是try catch处理掉,要么也是抛出去
  • method1选择本地 try catch 住,一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了

对象

将对象看作“服务提供者”,程序本身将向用户提供服务,它将调用其他对象提供的服务来实现这一目的。我们的目标是创建或寻找能够提供理想的服务来解决问题的一系列对象。

通过继承而产生的类型等价性是理解面向对象程序设计方法内涵的重要门槛。

一切都是对象,通过操作对象的一个引用来使用对象。

1
2
3
4
5
6
7
8
9
有五个地方存储对象:

- 寄存器:最快的存储区,但数量有限,不能直接控制,也无法在程序中感知到。
- 堆栈:位于通用RAM(随机访问存储器)中,可通过堆栈指针使用,指针的移动来进行分配或释放。存储对象引用。但Java系统需要知道所有在堆栈中的数据的生命周期。
- 堆:一种通用内存池,存放所有Java对象,不需知道数据的生命周期。但是分配和清理更慢一点。
- 常量存储:常量一般直接放在程序中,但若常量本身会与其他部分隔离开,则可以将其存放于ROM中。
- 非RAM存储:数据(如流对象,持久化对象)完全存活于程序之外,即把对象转化为存放在其他媒介上的事物,在需要时再进行恢复。

但是基本类型很小,常用,也用堆存放的话效率太低,所以创建一个非引用的自动变量来直接存储 “值”,并置于堆栈中。基本类型存储的是实际值,所以在赋值后,改变一个变量时彼此不会相互影响。而为对象赋值时(包括基本类型数组)就不行了,因为赋值实际上复制的是引用。

使用new创建一个对象时,它可以存活于作用域之外,但是引用将无法再作用域外使用。所以只需要创建对象,当不再使用时他们会自动消失,因为Java的垃圾回收器会监视new创建的所有对象,并辨别不再被引用的对象并释放他们的内存空间。

其中基本数据类型即使没有初始化也会有一个默认值,但是局部变量并没有

类中方法的参数传递的是对象的引用

每个编译单元(即一个.Java文件)都只能有一个public类,即只有一个单一的接口,如果不声明public,则为包访问权限。但不能声明为private或protected

包是一种解决命名冲突的方式,通过利用操作系统的目录层次方式来实现的,在CLASSPATH中通过目录查找方式来实现包中文件的查找。

复用类

组合和继承,代理(不直接支持)

  • 存在继承关系的子类构造时会先调用父类构造方法,当存在默认构造器(即无参数)时创建子类对象会自动创建基类的子对象,但是当没有默认构造器时,需要在子类构造方法中使用 super(参数) 显式调用基类构造器。
  • 代理介于组合和继承之间,在类中提供对象成员,并将其方法重写到该类中。
  • 在子类中重载(不是复写)父类的一个方法时,不会屏蔽父类的该方法。但是复写会屏蔽。
  • 判断用组合还是继承的一个方法是1. 看是否需要进行向上转型,若需要则必须继承。2. 用继承表达行为间的差异,用字段表达状态上的变化
  • final关键字:对基本类型使用时可以在编译期替换为常量,减轻运行负担。对对象引用使用时,final使引用恒定不变,但引用指向的对象自身可以被修改。一般使用final修饰方法为了锁定方法以防任何继承类修改它的含义。当将某个类整体定义为final时,表明不想任何类继承它
  • 类的初始化和加载:每个类的编译代码在需要使用时(初次使用时)才会被加载,通常发生在创建类的第一个对象时,但是当访问static域或static方法时也会加载。于是先初始化static方法或域,然后按正常步骤调用构造函数等等,,,

接口

  • interface关键字产生一个完全抽象的类,没有提供任何实现。只提供形式,包括方法名,参数,返回类型。是一个极端的抽象类。
  • interface内可包含域,隐式的声明为static final。接口内的方法一定是public
  • 使用接口是为了能向上转型为多个基类,同时防止创建该类的对象。如果要创建不带任何方法定义和成员变量的基类,那么就该选择接口而不是抽象类。
  • 继承是一个 “是不是”的关系,而 接口 实现则是 “有没有”的关系。对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。
  • 常用方法是在策略设计模式中,可以用任何想要的对象来调用我的方法,只要对象遵循我的接口
  • 优先选择类,如果接口很有必要,那么就进行重构。在打算组合的不同接口中使用相同的方法名通常会造成代码可读性变差,应尽量避免

内部类

  • 使用方法:从外部类的非静态方法之外的任意位置创建某个内部类的对象,必须具体指明这个对象的类型,外部类名字.内部类名字。当使用外部类直接创建内部类时(不使用方法返回内部类对象)要用外部类.new 内部类 来创建。
  • 生成的内部类对象可以直接访问外部类的所有元素。原理是当某个外部类创建一个内部类对象时,内部对象会秘密捕获一个指向外部类对象的引用。所以拥有外部类之前无法创建内部类对象(出了静态内部类)。然后当访问外部类成员时都是用那个引用来进行访问的,编译器会将这些细节隐藏。
  • 可以在内部类中实现一个接口,返回该接口的一个对象,那么就完全阻止了依赖对象的编码,完全隐藏了实现细节。
  • 可以在一个方法或任意作用域内定义内部类,称为局部内部类,可以在同一子目录下的其他内部类中使用该内部类。
  • 匿名类
  • 嵌套类
  • 为什么需要:每个内部类都能独立的继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类有效的实现了”多重继承“

多态

向上转型:

当父类引用调用子类对象时,所使用的是父类中的方法,而父类中的方法在子类中都存在,所以这样调用是安全的。相反子类引用指向父类对象时(向下转型),子类可能含有父类没有的方法,那么就不安全,需要在运行期进行类型检查。

绑定

实现多态就需要后期绑定,Java中除了static和final方法外,都是后期绑定的。所以static方法和final方法(private方法属于final)都不能重写。同时域访问操作也都由编译器解析,因此不是多态的。

构造和析构

  • 子类对象构造时需要调用父类构造函数生成一个父类对象,因为父类中的域只有他自己能访问进行初始化。
  • 如果在父类构造方法中调用一个方法(该方法在子类中被重写),此时由于子类构造方法还未调用,所以域还没有初始化,可能会造成问题。所以初始化的实际过程是在其他任何事物发生之前将分配给对象的储存空间初始化为二进制的零。之后的调用顺序和之前所说的一样。
  • 因此编写构造器时一条准则是:用尽可能简单的方法使对象进入正常状态;可以的话,避免调用其他方法。可以安全调用的方法是基类中的final方法。
  • 如果需要进行清理,则和C++中调用析构函数类似。在dispose方法中调用基类和组合类对象的dispose方法。

用继承还是组合:用继承表达行为间的差异,用字段表达状态上的变化

操作符

  • System.out.printl() 中String后如果有 + 号的话,会尝试将紧跟着的非String类转换为String类,但如果先执行的是个非String类,则执行完后再转换。
  • == 和 != 比较的是对象的引用,equals()比较的是对象中的内容,但是该方法不适用于基本类型,而且只能用在重写了equals() 方法的类中,因为该方法默认的是比较引用。
  • 不可将非布尔值作为布尔值在逻辑表达式使用,如 int a = 5; !a; 就是一个非法的语句
  • 进行窄化转化(narrowing conversion)必须显示进行转换
  • 对基本数据类型执行算术运算或位运算,只要类型比int小(如char, byte, short),就会在运算前自动转换为int

初始化和清理

垃圾回收器会自动回收那些由 new创建的对象,但没有通过new创建的对象则需要再类中定义一个 finalize() 方法。但是finalize不等于c+中的析构函数,在C++中对象一定会被销毁,但Java却不一定总是被垃圾回收

  1. 对象可能不被垃圾回收
  2. 垃圾回收并不等于析构
  3. 垃圾回收只与内存有关

使用垃圾回收器只为了回收程序不再使用的内存,所以使用finalize的只有一种特殊情况,即通过本地方法调用了C或C++代码以malloc分配的内存,此时需要在 finalize() 方法中调用free()函数来释放。

JVM不会浪费时间执行垃圾回收如果内存未耗尽的话

垃圾回收器的工作原理(P90):“停止—复制” 和 “标记—清扫” 两种模式自适应执行。都是从堆栈和静态区出发扫描所有的引用。但是 停止—复制 适用于垃圾较多时,标记—清扫 适用于少量垃圾。

容器

使用泛型制定了容器实例可以保存的类型,可在编译期避免将错误类型的对象放到容器中。

泛型转换

  • 子类泛型不能转父类泛型
  • 父类泛型也不能转子类泛型
  • 但是在使用 ?extends 通配符时可以将子类泛型转为父类泛型,但是父类泛型不能放东西只能取。
  • 使用 ?super 通配符时可以将父类泛型转为子类泛型,但是子类泛型不能取东西只能放。

划分为两个概念:

  • Collection:一个独立元素的序列,这些元素都服从一条或多条规则,Collection.addAll()只接受另一个Collection对象作为参数
  • Map:一组成对的键值对对象,允许使用键来查值

迭代器

也是一种设计模式,为了能实现处理不同容器类型间方法代码的复用。迭代器是一个对象,工作是遍历并选择序列中的对象。

并发

基本机制

  • 解决 速度 和 设计可管理性
  • 设计并发是因为阻塞存在
  • 编写多线程困难之一在于协调不同线程对资源的使用
  • 进程间并发简单很多,安全很多
  • 线程驱动任务,由Runnable接口提供,并编写run方法,然后将Runnable对象交给一个Thread构造器。Callabel接口调用call方法,有返回值。线程并不是任务,Thread类自身不执行任何操作,他只是驱动赋予他的任务。
  • 使用yield方法是暗示本线程工作做得差不多了,可以让给同级别的别的线程了,但只是建议不一定执行。
  • 后台线程(守护线程)是在程序运行时在后台提供通用服务的线程,并不是必须的。当程序所有非后台线程都结束时程序也就终止了。守护线程通常会被用来做日志,性能统计等工作。
  • 不能直接捕获线程中逃逸的异常,但可以使用Executor解决

共享资源

通过加锁,每个访问临界共享资源的方法都必须被同步。

使用并发时,要将域设为private,为了使synchronized关键字能防止其他类访问域。

不要依赖于原子操作

lock使用繁琐一点但是更加灵活,控制程度更高。

防止资源冲突的另一个方式是根除对变量的共享。线程本地存储是一种自动化机制,可以为使用相同变量的每个不同的线程都创建不同的存储。ThreadLocal对象通常当作静态域存储,只能通过get和set方法来访问

书籍

  • Java编程思想
  • Java并发编程实战
  • 深入理解JVM
  • 函数式编程思维
  • tcp/ip详解
  • 鸟哥的linux私房菜
  • spring mvc +mybatis开发从入门到精通
  • spring技术内幕
  • elasticsearch服务器开发
  • redis入门指南