应届生找工作要点总结(1)JAVA基础

getdeclaredmethods:返回method对象的一个数组,这些对象反应此class对象表示类或接口声明的所有方法,包括公有、私有、默认和保护方法,但不包括继承的方法。

getmethods:返回此class对象表示的类或接口的公共方法,包括父类继承的。

copyOnWriteArrayList和copyOnWriteSet从一开始大家都共享一个内容,当某人想要修改这个内容时,才会真正把内容复制出去,形成新内容再修改。并发读不需加锁。

copyOnWriteArratList核心:对于任何array在结构上有所改变的操作(add,move等),copyOnWriteArratList都会复制现有数组,再在复制的数组上修改。这样就不影响iterator中的数据,修改完成后改变现有数据引用即可。适合多读写少。

ArrayBlockingQueue:基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小。并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。

LinkedBlockingQueue:基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE。

PriorityBlockingQueue:以上2种队列都是先进先出队列,而PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即容量没有上限(通过源码就可以知道,它没有容器满的信号标志),前面2种都是有界队列。

DelayQueue:基于PriorityQueue,一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

put方法用来向队尾存入元素,如果队列满,则等待;take方法用来从队首取元素,如果队列为空,则等待;offer方法用来向队尾存入元素,如果队列满,则等待一定的时间,当时间期限达到时,如果还没有插入成功,则返回false;否则返回true;poll方法用来从队首取元素,如果队列空,则等待一定的时间,当时间期限达到时,如果取到,则返回null;否则返回取得的元素。

ArrayList和Vector,都实现了List接口,由数组实现;ArrayList安全,Vector不安全;ArrayList扩容50%,Vector扩容100%。

LinkedList由链表双向实现,ArrayList适合检索或在末尾增删,LinkedList适合在中间增删;LinkedList还实现了queue接口,提供peek(),poll(),offer()等方法,不抛异常。add(), remove(), element()抛异常。ArrayList和LinkedList的remove和contains方法都依赖于equals方法。

链表批量增加,是靠for循环遍历原数组,依次执行插入节点操作。对比ArrayList是通过System.arraycopy完成批量增加的。增加一定会修改modCount。通过下标获取某个node 的时候,(add select),会根据index处于前半段还是后半段进行一个折半,以提升查询效率。删也一定会修改modCount。 按下标删,也是先根据index找到Node,然后去链表上unlink掉这个Node。 按元素删,会先去遍历链表寻找是否有该Node,如果有,去链表上unlink掉这个Node。改也是先根据index找到Node,然后替换值。改不修改modCount。查本身就是根据index找到Node。所以它的CRUD操作里,都涉及到根据index去找到Node的操作。

hashset不是线程安全的,底层hash表,若两个元素的hash值不同,则一定不是同一个元素,若hash值相同,则判断equals,若equals相同,且hash值相同,则是同一个对象,若euqals不同,则不是一个对象(hashset的contains和remove依赖于euqals)。基于hashmap实现,hashset的元素都放在hashmap上,而value统一为private static final object PRESENT = new Object();允许null值,不允许重。add调用hashmap的put。

treeset集合的特点是可以对其中的元素进行排序,换句话说,一种情况是放进了treeset中的元素必须有比较性,集合是底层结构是二叉树,保证元素唯一性的方法是compareTo方法返回0。另一种情况是元素无比较性,但是集合有比较性,定义一个类,实现comparator接口,实现一个比较器。将元素放在红黑树。树总是平衡的,保证插入、删除、查询的复杂的O(logn)。

一个treemap中放的student,按student的age排序,把10个student放入treemap中,他们已经排好序了,若修改一个student的age,他们顺序没变。

class Student {

int age;

public Student(int age) {

this.age = age;

}

@Override

public String toString() {

return "Student [age=" + age + "]";

}

}

public static void main(String[] args) {

TreeMap<Student, Integer> treemap = new TreeMap<>(new Comparator<Student>() {

@Override

public int compare(Student o1, Student o2) {

return o1.age - o2.age;

}

});

Student student1 = new Student(1);

Student student2 = new Student(2);

Student student3 = new Student(3);

Student student4 = new Student(4);

Student student5 = new Student(5);

treemap.put(student4, 1);

treemap.put(student5, 2);

treemap.put(student3, 6);

treemap.put(student1, 2);

treemap.put(student2, 7);

System.out.println(treemap);

student1.age = 100;

System.out.println(treemap); // 顺序不变

}

{Student [age=1]=2, Student [age=2]=7, Student [age=3]=6, Student [age=4]=1, Student [age=5]=2}

{Student [age=100]=2, Student [age=2]=7, Student [age=3]=6, Student [age=4]=1, Student [age=5]=2}

HashTable和HashMap区别

继承的父类不同:Hashtable继承自Dictionary类(实现map、cloneable、serializable接口),而HashMap继承自AbstractMap类。但二者都实现了Map接口。

线程安全性不同:Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。Map m = Collections.synchronizedMap(new HashMap(...));

是否提供contains方法:HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey,因为contains方法容易让人引起误解。Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。

key和value是否允许null值:其中key和value都是对象,并且不能包含重复key,但可以包含重复的value。Hashtable中,key和value都不允许出现null值。但是如果在Hashtable中有类似put(null,null)的操作,编译同样可以通过,因为key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。

两个遍历方式的内部实现上不同:Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。

hash值不同:hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值。Hashtable计算hash值,直接用key的hashCode(),而HashMap重新计算了key的hash值,Hashtable在求hash值对应的位置索引时,用取模运算,而HashMap在求位置索引时,则用与运算,且这里一般先用hash&0x7FFFFFFF后,再对length取模,&0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号外改变,而后面的位都不变。此方法加入高位运算,防止低位不变高位变化时造成冲突。

内部实现使用的数组初始化和扩容方式不同:HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

在jdk1.8后,hashmap中,若链表长度太长(默认8),链表转为红黑树。元素小于64就扩容,大于64转红黑树。

HashMap为什么是线程不安全的

HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。在hashmap做put操作的时候会调用到以上的方法。现在假如A线程和B线程同时对同一个数组位置调用addEntry,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失。

resize在多线程的情况下可能产生条件竞争。因为如果两个线程都发现hashmap需要resize了,他们会同时试着调正大小。在调整的过程中,存储在链表中的元素的次序会反过来。因为移动到新位置时,hashmap并不会将元素放在链表尾部,而是放在头部,这是为了避免尾部遍历。(否则,针对key的hashcode相同的entry每次添加还要定位到尾节点)。如果竞争发生了,可能出现环形列表。之后,当我们调用get(key)操作时就可能发生死循环了。

扩容机制:扩容(resize)就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组。在扩充HashMap的时候,不需要像JDK1.7的实现那样重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”。这个设计确实非常的巧妙,既省去了重新计算hash值的时间,而且同时,由于新增的1bit是0还是1可以认为是随机的,因此resize的过程,均匀的把之前的冲突的节点分散到新的bucket了。

在使用迭代器的过程中其他线程修改了map,则会抛出concurrentmodificationexception,这就是所谓的fast-fail策略,是通过modcount实现,对hashmap内容的修改都将增加这个值,那么在迭代器初始化时会将这个值付给迭代器的expectedmodcount。在迭代过程中,判断modcount和expectedmodcount是否相等,若不相等,则说明其他线程修改了map,modcount是volatile的。

fast-fail:java集合的一种错误检查机制。当多个线程对集合上的结构进行改变的操作时,可能会fast-fail。比如存在线程x,y。x通过iterator遍历集合A中的元素。在某个时刻y修改了A的结构(结构上的修改,而不是简单修改内容)。这时会抛出concurrentmodificationexception,从而发生fast-fail。若单线程违反该规则,也会抛出异常。因为modcount与exceptedmodcount改变不同步。当iterator执行next()、remove(),要判断modcount和expectedmodcount是否相等,不相等要抛异常。

ConcurrentHashMap采用了非常精妙的"分段锁"策略,ConcurrentHashMap的主干是个Segment数组。Segment继承了ReentrantLock,所以它就是一种可重入锁(ReentrantLock)。在ConcurrentHashMap,一个Segment就是一个子哈希表,Segment里维护了一个HashEntry数组,并发环境下,对于不同Segment的数据进行操作是不用考虑锁竞争的。(按默认的ConcurrentLeve为16来讲,理论上就允许16个线程并发执行)。所以,对于同一个Segment的操作才需考虑线程同步,不同的Segment则无需考虑。

Segment类似于HashMap,一个Segment维护着一个HashEntry数组。HashEntry是目前我们提到的最小的逻辑处理单元了。一个ConcurrentHashMap维护一个Segment数组,一个Segment维护一个HashEntry数组。

初始化方法有三个参数,如果用户不指定则会使用默认值,initialCapacity为16,loadFactor为0.75(负载因子,扩容时需要参考),concurrentLevel为16。Segment数组的大小ssize是由concurrentLevel来决定的,但是却不一定等于concurrentLevel,ssize一定是大于或等于concurrentLevel的最小的2的次幂。比如:默认情况下concurrentLevel是16,则ssize为16;若concurrentLevel为14,ssize为16;若concurrentLevel为17,则ssize为32。为什么Segment的数组大小一定是2的次幂?其实主要是便于通过按位与的散列算法来定位Segment的index。

put的主要逻辑也就两步:1.定位segment并确保定位的Segment已初始化 。2.调用Segment的put方法。Segment中的put方法是要加锁的。只不过是锁粒度细了而已。

get方法无需加锁,由于其中涉及到的共享变量都使用volatile修饰,volatile可以保证内存可见性,所以不会读取到过期数据。

ConcurrentHashMap的size操作:在累加count操作过程中,之前累加过的count发生变化的几率非常小,所以ConcurrentHashMap的做法是先尝试2次通过不锁住Segment的方式来统计各个Segment大小,如果统计的过程中,容器的count发生了变化,则再采用加锁的方式来统计所有Segment的大小。那么ConcurrentHashMap是如何判断在统计的时候容器是否发生了变化呢?使用modCount变量,在put , remove和clean方法里操作元素前都会将变量modCount进行加1,那么在统计size前后比较modCount是否发生变化,从而得知容器的大小是否发生变化。

error和exception:error类一般指与虚拟机相关的问题,比如系统崩溃,虚拟机错误,内存不足,对于这样的错误,建议程序终止。exception表示程序可以处理的异常,遇到这类异常应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

运行异常:都是RuntimeException类及其子类异常,如NullPointerException, IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。常见的runtimeException:空指针、数组越界、类型转化,除0、并发修改异常,rejectedExcutionExceotion。

非运行时异常(编译异常,检查异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。

final定义的常量,一旦初始化,不能被修改,对基本类型来说是其值不可变,对引用来说是其引用不可变。其初始化只能在两个地方:其定义处;构造函数中,二者只能选其一,不能在定义时给了值,又在构造函数中赋值。一个类不能既被声明为abstract,也被声明为final。

不管有无异常发生,finally总会执行,若catch中有return语句,也执行,在return之前执行。

finalize方法定义在object中。

被动引用:通过子类引用父类静态字段,不会导致子类的初始化,即父类静态代码块执行,子类静态代码块不执行。通过数组定义来引用类,不会触发此类的初始化,如:Person p[] = new Person[10]。

泛型,即参数化类。泛型擦除:java编译器生成的字节码文件不包含泛型信息,泛型信息将在编译时被擦除,这个过程称为泛型擦除。其主要过程为:将所有泛型参数用其左边界(最顶级的父类型替换)类型替换;移除所有类型参数。http://www.runoob.com/java/java-generics.html。泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。

StringBuffer内部实现的是char数组,默认初始长度为16,每当字符串长度大于char数组长度时,jvm会构造更大的char数组,并将原来的数组内容复制到新数组。

String s = "abc" + "de";创建一个对象,在编译期间就能确定字符串的情况下,使用+的效率最高。每一次+= “a”,都要创建一个对象。

String s1 = "abc" + "def" + 47 + "mm";编译器使用了StringBuilder的append方法,最后调用toString。

String s1 = new String("abc"); // 执行时,“abc”本来就是池中的对象,而在运行时执行new String("abc")时,将池中的对象复制一份放入堆中,并且把这个对象的引用交给s1持有,这条语句就创建了2个对象。

String s2 = new String("abc"); // 执行后,一共创建了3个对象。

public synchronized StringBuffer append(CharSequence s, int start, int end) {

super.append(s, start, end); // 数字表示的是开始和结束

return this;

}

public synchronized StringBuffer append(char str[], int offset, int len) {

super.append(str, offset, len); // 数字表示的是开始位置和获取字符的长度

return this;

}

在字符串的连接过程中StringBuffer的效率要比String高,string操作代码:String str = new String("welcome to ");str += "here";如上代码实际上是通过建立一个StringBuffer,让后台调用append(),最后再将StringBuffer toSting();这样的话String的连接操作就比StringBuffer多出了一些附加操作,当然效率上要打折扣。并且由于String对象是不可变对象,每次操作Sting都会重新建立新的对象来保存新的值。这样原来的对象就没用了,就要被垃圾回收。这也是要影响性能的。还有一点就是JAVA中虽然有垃圾回收机制,但是不是瞬间回收的,所以也会造成内存资源的浪费。

  只有string重载了“+”操作,对这个操作符,专门写了个方法,api解释stringbuilder或者stringbuffer。但是stringbuilder不是线程安全的,而stringbuffer是线程安全的,所以在字符串频繁拼接是建议使用stringbuffer。少量拼接用string即可。

StringBuffer(同步的)和String(不可变的)都是线程安全的,StringBuilder是线程不安全的;String是不可变的,StringBuilder和StringBuffer是可变的;String的连接操作的底层是由StringBuilder实现的;三者都是final的,不允许被继承;StringBuilder 以及 StringBuffer 都是抽象类AbstractStringBuilder的子类,它们的接口是相同的。

1)如果操作少量的数据用String。2)单线程下操作大量的数据用StringBuilder。3)多线程下操作大量的数据用StringBuffer。

Arrays.sort对于基本数据类型用快速排序,对象数组用改进的归并。

collection.sort(list<T> list)按自然顺序排序,元素必须有可比较性,实现comparable接口。collection(List<T> list, comparator<q super T>)。

collection类中提供了一个方法,返回一个同步版本的hashmap,该方法返回的是一个synchronized map的实例,synchronized map类是定义在collections中的一个静态内部类。它实现map接口,并对其中的每个方法通过synchronized关键字进行了同步控制。但其在使用过程中不一定线程安全。Map m = Collections.synchronizedMap(new HashMap(...));

内部类一共有4种。成员内部类:外部类的成员;局部内部类;静态内部类,类似于静态成员;匿名内部类:实现一个接口或者继承一个抽象类。

外部类不能任意访问内部类成员,要做到这一点,外部类必须创建其内部类对象(静态内部类除外)。内部类可以访问所有外部类成员,因为内部类就像一个类的成员。

内部类拥有其外围类的所有元素访问权,包括private,包括方法和字段。构造内部类对象时,需要一个指向外围类的引用(static内部类除外),这个引用编译器已经帮你做了。

非静态内部类用外部类对象创建内部类对象:new outer().new Inner();

private不能被继承。private方法默认是final的,不能被覆盖。java实现封装用private。

接口和抽象类应用场景:

标记接口:什么抽象方法都没有。

需要实现特定多项功能,而这些功能之间完全没有联系。

定义了一个接口,但又不想强迫每个实现类都必须实现所有的抽象方法,所以可以用抽象类来实现一部分方法体,比如adapter。

抽象类是简化接口的实现,它不仅仅提供了公共方法的实现,让我们可以快速开发,又允许你的类完全可以实现所有的方法,不会出现紧耦合的情况。把手机定义为一个抽象类,它是各种不同手机类的抽象,有些手机有指纹解锁,有的有防尘防水,对于附加的功能,可以分别定义为接口,当需要这些功能时,再去实现。

抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;一个类只能继承一个抽象类,而一个类却可以实现多个接口。一个抽象类可以没有抽象方法。

当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。抽象类的功能要远超过接口,但是,定义抽象类的代价高。因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类中,你必须继承或编写出其所有子类的所有共性。

Objetc的clone方法:public Object Clone() throws CloneNotSupportedException创建并返回此对象的一个副本,对于任意对象x,则x.clone() != x是true,x.clone().getClass() == x.getClass()是true,这些并非必须满足的条件。

首先,若对象的类不能实现cloneable接口,则会抛出CloneNotSupportedException(所有数组都被视为实现了cloneable接口)否则,此方法会创建次对此对象的类的新实例,严格使用此对象相应字段的内容初始化该对象的所有字段,这些字段的内容没有被自我复制,所以此方法执行是对该对象的浅复制,而非深复制。

Object类没有实现cloneable接口,所以在类为Object的对象上调用clone方法时会抛出异常。cloneable是一个标记接口。

浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝包含的引用指向的对象。深拷贝不仅拷贝对象,而且拷贝对象包含的引用所指向的对象。比如,对象a1中包含对象b1的引用,b1包含c1的引用,浅拷贝a1得a2,则a2中任然包含b1的引用,b1中包含c1引用。深拷贝是浅拷贝的递归,若深拷贝a1得a2,则a2包含b2(b1的拷贝),b2中包含c2。

java的序列化运算:当前类的描述;当前类属性的描述;父类描述;父类属性描述;父类属性值描述。类描述是从下到上,类属性描述是从上到下。

String、数组、Enum实现了序列化接口,从writeobject方法看出来。

序列化一个对象,只要让他实现serializable接口(该接口没有方法,是一个标记接口),默认序列化机制,不仅会序列化本身,还会对其引用的其他对象序列化。

静态不能被序列化,因为它不在堆里,用transient修饰的也不能序列化(即使在堆中),使用ObjectInputStream和ObjectOutputStream。

class Person implements Serializable {

private static final long serialVersionUID = 1L;

String name;

int age;

public Persion(String name, int age) {

super();

this.name = name;

this.age = age;

}

@Override

public String toString() {

return "Person [name]" + name + ", age=" + age + "]";

}

}

public static void writeObj() throws FileNotFoundException, IOException {

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));

oos.writeObject(new Person("lisi", 38));

oos.close();

}

public static void readObj() throws FileNotFoundException, IOException, ClassNotFoundException {

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));

Object o = ois.readObject();

Person p = (Person) o;

System.out.println(p);

}

writeObj();

readObj();

hash是把任意长度的输入,通过散列算法,变成固定长度的输出,不同输入可能同输出。hash函数里不能有随机函数。

作用:文件校验,数字签名。

hash冲突解决办法:链地址法;开放地址法:线性探测,当前冲突则往后一个,再冲突再往后,直到合适的;二次探测;随机探测。

语法糖,在计算机语言中添加的某种语法。这种语法对语言功能并没有影响,但是更方便程序员使用。java中常见的语法糖有:泛型、变长参数、自动拆箱/装箱。虚拟机运行时不支持这些语法,他们在编译阶段还原回简单的基础语法结构,这个过程称为解语法糖。包装类的“==”运算,在不遇到算数运算的情况下,不会自动拆箱,遇到了就自动拆箱。

https://www.cnblogs.com/dolphin0520/p/3780005.html

装箱拆箱:是java早年设计缺陷。基础类型是数据,不是对象,也不是Object的子类。需要装箱才能和其他Object的子类共用同一个接口。

ascii中,一个英文字母占一个字节,一个汉字占两个字节。utf-8中,一个英文1个字节,一个中文3个字节。unicode,中英文都2字节。在java中,字符只以一种形式存在,就是unicode。reader和writer使用的是gbk编码。a对应的ascii码为97,A是65。

基本数据类型double判断不能用==;存储精度问题,一般使用阈值,a-b小于这个阈值,一般就认为相等。

JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。

jdk1.8bin目录下:java.exe javac.exe javadoc.exe jar.exe jstack.exe(打印所有java线程堆栈跟踪信息)

jdk和jre:jdk的bin下有javac,jre的bin下没有javac。

面向对象和面向过程:

面向过程优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;嵌入式开发,linux等一般采用面向过程,性能重要的因素。缺点:没有面向对象易于维护,复用,扩展。

面向对象优点:易维护,复用,扩展,由于面向对象有封装,继承,多态性,可设计出低耦合的系统。缺点:性能比面向过程低。

继承的目的是让子类复用父类的非私有成员变量和成员函数;多态是让父类引用持有子类引用。

多态有什么好处?有两个好处:实现机制是重载和覆盖。

1. 应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。大大提高程序的可复用性。// 继承

2. 派生类的功能可以被基类的方法或引用变量所调用,这叫向后兼容,可以提高可扩充性和可维护性。 // 多态的真正作用

多态的实现原理:多态是基于Java的动态绑定,Person p = new Student();如果是静态编译的话,那p就是Person的引用,那就只能调Person的方法。如果是动态绑定的化,p实际上是指向Student这个在堆上的对象的,那么p调用一个方法其实是调用这个对象的方法,但是无法调用这个对象特有的方法(父类中没有的方法),这样编译都不会过。

再来看动态绑定这四个字:动态绑定体现在运行的时候才知道这个引用指向的是哪个对象。

断言:是软件开发中一种常用的调试方式,在实现中assertion就是程序中的一条语句,它对一个bool表达式进行检查,一个正确的程序必须保证这个bool表达式为true,若该值为false,则说明程序已处于不正确的状态,系统将给出警告并退出。

什么时候用断言,可以在预计正常情况下不会到达的任何位置上放置断言,断言可以用于验证传递给私有方法的参数。不过,断言不应用于验证传递给公有方法的参数。因为不管是否启用了断言,公有方法都必须检查参数,不过既可以在公有方法中,也可以在非公有方法中用断言测试后置条件。另外,断言不应该以任何方式改变程序状态。

int num = 32; S.o.p(num >> 32);结果为32,因为移位操作符右边的参数要先与32进行模运算,所以num>>32等价于32>>0,num>>33等价于num>>1。

不管java传递的参数类型是什么,一律传递参数的副本,若java是传值,那么传递的是值的副本,若传递的是引用,则传递的是引用的副本。

在java中,有时会遇到子类中成员变量或者方法和父类同名情况。因为子类中成员变量或方法的优先级高。所以子类中的同名成员变量或方法就隐藏了父类的成员变量或方法,但父类的成员变量和方法仍存在,可用super显式调用。

java中的大数类。BigInteger该类在java.math包中,其中一个构造器是new BigInteger(String s),将BigInteger的十进制字符串转化为BigInteger,若要将int型转为BigInteger,则BigInteger two = new BigInteger("2");BigInteger one = new BigInteger("1");one.add(two);

int 和 Integer有啥区别:1、Integer是int提供的封装类,而int是Java的基本数据类型;2、Integer默认值是null,而int默认值是0;3、声明为Integer的变量需要实例化,而声明为int的变量不需要实例化;4、Integer是对象,用一个引用指向这个对象,而int是基本类型,直接存储数值。

randomaccessfile:用来访问那些保存数据记录的文件,可用seek方法进行访问,并进行读写。这些记录的大小不必相同,但大小和位置必须可知,该类仅限于操作文件。randomaccessfile不属于inputstream和outputstream类系,直接继承object。随机访问文件的行为类似于存储在文件系统中的一个大型的byte数组,存在一个光标。

randomaccessfile构造函数,两种模式:“r”,只读,不会创建文件,会去读一个已经存在的文件,若文件不存在则抛异常。“w”读写模式,文件不存在自动创建,存在也不会覆盖。

创建实例的方法:new;class.forname().newInstance;clone()。

java五个特性:面向对象,跨平台,多线程,健壮性,解释性。

system.out返回printstream对象,println()是该对象中的方法。

javadoc生成的文档只能是public和protected成员生成注释。

本地方法是由非java编写的,可以使用java调用c或c++代码,这时处于性能考虑或者访问底层操作系统。

Dog dog = new Dog();这句话的执行过程:加载dog的class文件。初始化所有静态(包括static变量和静态代码块),并要按顺序。默认初始化。显式初始化。构造代码块。构造函数。

java程序初始化工作执行顺序如下:父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数。

java集合

Collection:List,Set

Iterator it = collection.iterator(); // 获得一个迭代子

    while(it.hasNext()) {

      Object obj = it.next(); // 得到下一个元素

    }

Listiterator是一个更加强大的iterator子类型,只能用于各种list访问。

List:LinkedList,ArrayList,Vector:Stack(Vector非常类似ArrayList,但是Vector是同步的)。

Map:Hashtable,HashMap,WeakHashMap(WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收)。

反射:允许程序进行自我检查,同时也允许对其内部成员进行操作。主要功能:得到一个对象所属的类,获取一个类的所有成员变量和方法,在运行时创建对象,在运行时调用对象的方法。

Class c1 = Class.forName("Employee"); // 第一种方式

Class c2 = Employee.class; // 第二种方式,java中每个类型都有class 属性。

Employee e = new Employee(); // 第三种方式,java语言中任何一个java对象都有getClass 方法

Class c3 = e.getClass(); // c3是运行时类 (e的运行时类是Employee)

java设计原则

单一职责原则:一个类只负责一种职责,只有这种职责的改变会导致这个类的变更。

里氏替换原则:只要父类出现的地方,都可以用子类替换,并且不会对程序造成影响,在实现上来说就是子类不要覆盖父类的非抽象方法,但可以重载。重载时需要注意,入参的要求要比父类宽松(保证可以进入),返回要比父类更加严格(保证出去不会有问题),这也正是实现里氏替换的基础。

依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象,翻译一下就是面向接口编程;接口一般是行为的集合,也就是尽可能的对行为抽象。抽象不应该依赖细节,细节应该依赖抽象。

接口隔离原则:翻译一下就是接口的功能尽可能单一,接口本质上是两个类之间关系的纽带,关系中不需要有的,在接口中不应该体现。

迪米特法则(最少知道原则):也就是说一个对象要对其他对象保持尽可能少的了解,即低耦合性,低耦合可以最大限度的保证代码的可复用性。这个实际上是针对被依赖的类来说的,对于被依赖的类,尽可能的将复杂的逻辑封装起来,对外只提供public方法,外部不需要知道内部的逻辑。

开闭原则:尽量通过扩展来面对需求的更改或者系统的变化,尽量不要对原有内容修改。

猜你喜欢

转载自blog.csdn.net/SEUSUNJM/article/details/86534446