而世之奇伟、瑰怪、非常之观,常在于险远,而人之所罕至焉
对象导论 书上的总结,写给自己看的,对于别人没有任何营养
对象
当试图开发一个程序时,最好的办法之一就是把对象当作服务提供者,一个对象只做一件事情。 创建者应该只提供接口给端程序员,隐藏实现细节,让端程序员减少混乱。 我们举个例子,商店是一个类,商店只提供出售这个接口,商店本身有进货,雇员工等方法。 但是商店并不需要提供这些细节给客户,客户在买东西的时候并不需要知道这些细节。
复用
复用是OOP最了不起的优点之一。我们一般有组合和继承两种方法来实现复用。(组合优于继承) 在考虑应该选择复合还是继承时我们需要先造个句
- A is B 使用继承,猫是动物
- A has B 使用复合,车有发动机
只是总结一些我对java知识点的遗漏
对象储存的位置
Java中可以动态分配内存来new对象,那么对象会被放在什么地方呢 计算机中有堆和堆栈,对象被储存在堆栈中
- 堆栈的分配非常迅速,只需要用汇编指令改变堆栈指针即可。但是存在这里面Java必须要知道所有内容的生命周期,而且堆的空间比较小。所以一般只有引用会存在堆栈里。
- 堆用于存放对象,灵活性非常大,但是性能低一些。
Java doc
Java doc开始于/**
,结束于*/
注意,javadoc只为public和protected成员进行文档注释。private和包内可见的注释会被忽略掉。用-private参数可以导出。
常量与运算符
equals默认实现是比较引用指向的对象是否是同一个。
指数计数法 1.3X10^5可以计为1.3e5
位移操作符
- <<算数位移
- <<<逻辑位移
1+2+3+“”
得到123,因为当编译器发现有字符串类型时,会先全部转成字符串再相加。
对char byte short运用操作运算符会转成int
方法的变量不会自动初始化,类的域会。但是还是不要让他自动初始化了= =。
Java中并没有goto,取而代之的是break label和continue label。 标签只能声明在循环上一行。方便我们跳出循环。 ##构造对象 多个构造函数用不同的参数来区分,甚至顺序不同也是可以区分出来,但正常情况下不要用,会让代码难懂
this构造方法
this()可以调用构造方法。可以减少重复代码。
- this()只能被调用一次
- this()只能在构造函数第一行被调用
this还可以在内部类中用来引用外部类。
#!java
public class Student {
private String name;
private String id;
Student(String name) {
this.name = name;
}
Student(String name, String id) {
this(name);
this.id = id;
}
}
垃圾回收杂谈
首先我们要搞清楚一个概念,当一个对象可以被回收时,对象并不会被马上回收甚至都不一定会被回收,只有当内存不够用或者程序退出的时候才会回收。 我们看一下几种垃圾回收的方法。 ###引用计数 每一个对象持有一个计数器,当引用连接时计数器+1,当引用离开作用域时或者被置为null时-1 垃圾回收器会把全部的对象遍历一遍,回收引用为0的对象。因为这种方式比较慢所以java不用它。(喂那你说他干什么) ###自适应 自适应垃圾回收技术会在两种模式之间切换。
- 暂停复制,暂停程序,把存活的对象从一块内存中复制到另外一块内存。这样一来对象就紧密的排列在一起了,不用遍历找到空的空间来new对象。
- 标记-清扫,从堆栈和静态存储区域出发,遍历所有引用,标记出存活对象然后清除没有标记的对象。这种模式下剩下的堆空间不连续,而且速度慢。 当有大量垃圾的时候会采用第一种,少量垃圾采用第二种。
枚举类型
枚举类型中的变量每一个都可以看作是一个单独的类。由于枚举和switch都是从有限中的数量来选择的,所以枚举和switch是绝配。
对象初始化之时
static对象只有在第一次被引用或者持有他的对象被引用的时候会被初始化
A类中有static B
,当A被引用的时候,B才会new出来。
在类中对象没有初始化会被置为默认值,在方法中没有初始化则不会有默认值,而且引用会使编译通不过。
#!java
public class Test {
static String a;
public static void main(String[] args) {
System.out.print(a); //返回null
}
}
#!java
// 编译失败
public class Test {
public static void main(String[] args) {
String a;
System.out.print(a);
}
}
###数组的初始化 初始化数组 最后一个数组中,可以多出逗号。
#!java
int[] a = new int[50];
int b[] = new int[50];
int[] c = new int[] {1,2,};
初始化有四种写法---孔乙己
- 在定义对象的地方,会在构造器被调用之前初始化
- 在类的构造器中
- 惰性初始化,在被使用之前初始化。用法:
if(xxx==null) {初始化}
- 用实例初始化(在大括号中初始化)
#重写后父类的方法被覆盖,即使向上转型也是调用子类重写后的方法,想要调用父类原来的方法也只能采用反射 写了这么久java连这个都没搞清楚的我= =
#!java
public class Student extends Person {
@Override
void sleep() {
System.out.print("Student sleep");
}
}
class Person {
void sleep() {
System.out.print("person sleep");
}
static void sleepPerson(Person person) {
person.sleep();
}
}
关于代理和装饰器模式
代理和装饰者是相反的,代理限制类的方法访问,减少提供的功能。装饰器则是增加了类的功能 待填坑
final
Java中final保证能在引用前被初始化。 final方法在java早期中,会被作为内嵌方法,但是现在的jvm中会自动识别,所以没说没卵用。 final方法只有当你的方法不想被重载时才需要用到。
访问权限控制
访问权限控制本质上是把动的事物和静的事物分开来。作为一名类库设计人员,你要尽可能把一切设置为private,只提供愿意让客户端程序员访问的方法。 打个比方,你设计一只猫,当它饿了的时候会喵喵叫
#!java
public class Cat {
public boolean hungry = false;
public String meow= "喵喵喵";
public void meow() {
System.out.println(meow);
}
}
你的本意是好的,但是由于你设计有问题,如果有傻逼程序员这样用你的代码
#!java
System.out.println(new Cat().meow);
这样运行也没有什么问题。 但是你突然发现了一个bug!忘记判断是否饿了了 于是你加上去
#!java
public class Cat {
public boolean hungry = false;
public String meow= "喵喵喵";
public void meow() {
if(hungry)
System.out.println(meow);
}
}
但是之前那个傻逼程序员的程序运行就不正常了。 如果你一开始设计猫的叫声为不可访问,程序员就必须通过meow()方法来调用猫叫了,你可以随心所欲的修复问题而不担心别人的程序被破坏。 这里动的事物是内部实现,而静的事物是对外提供的方法。
类不能是 private 或者 protected 的,内部类例外,如果你不想类被别人创造,则用private构造器即可
##多态 Java中除了static方法和final方法,其他的方法都是后期绑定的。,这里注意方法,域不是动态绑定的,访问域的操作由编译器解析。
#!java
public class Point {
public int i = 0;
protected int get() {
return i;
}
}
class Poi extends Point {
public int i = 1;
@Override
protected int get() {
return i;
}
}
main方法
Poi poi = new Poi();
System.out.println(poi.get()); // 1子类的方法
System.out.println(poi.i); // 1子类的域
Point point = (Point)poi;
System.out.println(point.i); // 0父类的域
System.out.println(point.get()); // 1子类的方法
由此可以看出,只是方法才会被动态绑定。
当有父类方法在构造方法中调用被覆盖的方法,初始化子类的时候会怎样呢?
- 覆盖父类方法
- 调用父类初始化方法,期中调用的方法是子类的方法
- 子类的域被初始化
- 调用子类构造方法
Java SE5中,有一个Son继承Father, Father有一个返回Father
对象的方法,当Son覆盖这个方法时可以返回Son
对象
接口
抽象类
如果一个类包含一个或者多个抽象方法,则一定要被声明为抽象的。当然不是所有的方法都必须是抽象的。 ###接口的域 接口中所有定义的待实现方法都是public的,无论你写不写public。 接口可以带有域和方法,不过他们都是static和final的。
实现多个接口
一个类可以实现多个接口并且转型为每个接口,每个接口都是一个单独的类型。
Java中类不能多重继承,但是接口可以哟
interface A extend B,C,D,E,F,G {
}
适配器模式 https://www.cnblogs.com/denisyxc/archive/2009/07/29/1533734.html 接口实现策略模式 https://blog.csdn.net/chenjie19891104/article/details/6396458 接口实现回调 https://www.cnblogs.com/xrq730/p/6424471.html
Java中的容器
Collection的方法
contains判断是否含有这个元素,containsAll可以求出两个Collection是否为包含关系 retainAll可以求出两个Collection之间的交集。 ---上面的方法和顺序都没有关系--- shuffle打乱Collection,sort排序。
toArray方法可以转成一个数组
对于List来说,有subList可以取出子集。
Collection的初始化
Collection有
- Arrays.asList()方法,接受可变参数,可以在构造时初始化完全部元素,但是由于底层是数组实现的,所以不能调整尺寸
- 接受一个Collection,用来初始化
- addAll方法,推荐使用这个
Map比较特殊,除了接受另外一个Map外没有其他的初始化形式。
各种Collection之间的区别
List
ArrayList 底层由数组实现,一开始创建10个元素的数组,当不够用的时候会自动扩容并且把原来的元素复制过去,随机访问元素较快,插入和删除慢。 LinkedList 底层是由链表实现的,所以插入和删除元素较快,提供了顺序访问的优化。
LinkedList的特殊用法 通过查看源码,我们可以看得出来,LinkedList实现了Deque接口,Deque继承了Queue接口。 那么就是说,我们可以把LinkedList向上转型成Queue来使用。
转型成queue后
返回null或者false | 抛出异常 | |
---|---|---|
插入 | offer | add |
查看队首 | peek | element |
查看并且删除队首 | poll | remove |
PriorityQueue 好像没什么可以讲的
Set
TreeSet
使用红黑树实现,有顺序,O(logN)HashSet
散列表实现,查找飞快,常数复杂度LinkedHashSet
继承自HashSet用链表维护顺序
迭代器
迭代器是我非常喜欢的一个东西,在Java中仅次于toString了。 这里注意一下数组不会自动转成Iterable类型。 当类实现了迭代器会非常顺手。Collection都实现了Iterable接口,我们可以写一个方法,它不需要知道容器类型
#!java
static <T> void display(Iterable<T> iterable) {
for(T i:iterable) {
System.out.println(i);
}
}
可以说,迭代器统一了容器的访问形式。
自定义迭代器
一般想用for each来遍历我们的类,我们可以实现Iterable
接口,在接口中实现iterator
方法,返回一个Iterator
对象
class My<T> implements Iterable<T> {
@Override
public Iterator iterator() {
return new Iterator() {
@Override
public boolean hasNext() {
return false;
}
@Override
public Object next() {
return null;
}
};
}
}
当然,当需要多种顺序的时候,我们可以提供getIterable
方法, 在方法中返回实现了Iterable的内部类即可。
interface Interface {
void doSomething();
}
class RealObject implements Interface {
@Override
public void doSomething() {
System.out.println("do something");
}
}
class Proxy implements Interface {
private Interface proxied;
public Proxy(Interface proxied) {
this.proxied = proxied;
}
@Override
public void doSomething() {
System.out.println("dododo");
proxied.doSomething();
}
}
public class Main {
public static void main(String[] args) {
RealObject object = new RealObject();
Proxy proxy = new Proxy(object);
proxy.doSomething();
}
}
剩下的分篇整理在本地文件里了。
本文由 鸡米 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Oct 27,2019