面向对象特征
封装,继承,多态
封装:将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。
继承:这是类与类之间的关系,子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法,并且只有单继承
多态:对象拥有多种形态,即引用多态和方法多态;引用多态中,父类的引用可以指向本类对象,也可以指向子类的对象;方法多态中,创建本类对象时,调用的方法为本类的方法,创建子类对象时,调用的方法为子类重写的方法;多态的前提是继承和重写
基本数据类型
整数值型:byte,short,int,long
浮点型:float,double
字符型:char
布尔型:boolean
整数默认int型,小数默认是double型。Float和long类型的必须加后缀。
String属于引用类型,不是基本类型
平时说的拆箱和装箱就是引用类型和基本类型的转换,基本类型转换成引用类型后就可以调用其包装类的方法进行类型转换
Final关键字
#####final修饰类
该类不能被继承
#####final修饰属性
该属性一旦赋值就不能被更改,即此时该属性为常量
#####final修饰方法
该方法可以被调用,不能重写
static关键字
#####static方法
static方法就是没有this的方法。在static方法内部不能调用非静态方法,因为非静态方法是在对象创建的时候初始化的,静态方法在类加载的时候就初始化了;反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。
static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的
#####static变量
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static成员变量的初始化顺序按照定义的顺序进行初始化
#####static代码块
static代码块也叫静态代码块,通常是把只进行一次的初始化操作放在代码块中以优化程序性能,避免重复开辟内存。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次
#####static类
static可以用来修饰内部类,也就是静态内部类;可以避免非静态内部类持有外部类引用导致内存泄漏
volatile
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
禁止进行指令重排序,保证有序性
该关键字可参考volatile
finalize和finally
finalize 方法是在对象被回收之前调用的方法,给对象自己最后一个复活的机会,但是什么时候调用 finalize 没有保证。
finally 是一个关键字,与 try 和 catch 一起用于异常的处理。finally 块一定会被执行,无论在 try 块中是否有发生异常
抽象类和接口区别
接口是对动作的抽象,抽象类是对根源的抽象
抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象
抽象类要被子类继承,接口要被类实现
接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量
抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的类,如不能全部实现接口方法,那么该类也只能为抽象类
抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
抽象类里可以没有抽象方法
如果一个类里有抽象方法,那么这个类只能是抽象类
抽象方法要被实现,所以不能是静态的,也不能是私有的
接口可继承接口,并可多继承接口,但类只能单继承
重写和重载
重写是作用于父类的方法。重载是作用于自己的方法
方法重写要求参数列表必须一致,而方法重载要求参数列表必须不一致
方法重写要求返回类型必须一致(或为其子类型),方法重载对此没有要求
方法重写只能用于子类重写父类的方法,方法重载用于同一个类中的所有方法
方法重写对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制
父类的一个方法只能被子类重写一次,而一个方法可以在所有的类中可以被重载多次
重载是编译时多态,因为编译器可以根据参数的类型来选择使用哪个方法;重写是运行时多态,因为编译期编译器不知道并且没办法确定该去调用哪个方法,JVM会在代码运行的时候作出决定
Runnable和Callable的区别
Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。而Callable+Future/FutureTask却可以方便获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务。
equals和==的区别
最大的区别是:equals是方法,==是运算符
等号在比较基本数据类型时比较的是值,而用等号比较两个对象时比较的是两个对象的地址值
类如果没有重写equals()方法,调用equals()方法其实和使用等号的效果一样,可查看Object类的equals方法源码实现,也是使用等号比较两个对象的地址值;
重写了equals()方法,就具体看重写逻辑,通常是比较两个对象的值,可查看String类的equals方法源码,就是比较每个字符是否相等
Java中的HashMap的工作原理是什么?
Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法来从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果key已经存在了,value会被更新成新值。
HashMap和Hashtable有什么区别?
HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。但是,他们有以下不同点:
HashMap允许键和值是null,而Hashtable不允许键或者值是null。
Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
ArrayList和LinkedList不同点:
ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。
与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,
在这种情况下,查找某个元素的时间复杂度是O(n)。
b相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,
不需要像数组那样重新计算大小或者是更新索引。
LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
大O符号描述了当数据结构里面的元素增加的时候,算法的规模或者是性能在最坏的场景下有多么好。
大O符号也可用来描述其他的行为,比如:内存消耗。因为集合类实际上是数据结构,我们一般使用大O符号基于时间,
内存和性能来选择最好的实现。大O符号可以对大量数据的性能给出一个很好的说明。
String StringBuffer StringBuild
String类中使用字符数组保存字符串, 有“final”修饰符,所以可以知道string对象是不可变的,在运行时保存了一个字符串池(String pool)
字符串池的实现可以在运行时节约很多heap空间,不同的字符串仅仅只保存一个
可以避免网络安全问题,多线程并发安全问题,数据库的用户名、密码都是以字符串的形式传入
socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,
否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,
没有用final修饰符,可知这两种对象都是可变的。
String中的对象是不可变的,也就可以理解为常量,显然线程安全。
对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。
在大部分情况下StringBuilder> StringBuffer > String
List遍历
第一种:
for(object o :list)
{
}
第二种:
Iterator iter = list.iterator();
while(iter.hasNext()){
Object o = iter.next();
}
第三种:
int size = list.size();
for(int i=0; i< size; i++){
Object o= list.get(i);
}
在数据量达到百万级甚至千万级的时候,第三种是最快的,第一种和第二种差不多,第二种稍微好点;数据量小的时候没有明显差别
Map遍历
第一种:
//遍历key 通过key获取value
for(Integer key:map.keySet()){
System.out.println(key);
String value = map.get(key);
}
第二种:
//遍历value 只能获取value
for(String value:map.values()){
System.out.println(value);
}
第三种:使用的较多
for(Map.Entry< Integer, String> entry : map.entrySet()){
entry.getKey();
entry.getValue();
}
第四种:可以在遍历的时候删除key,其它方式不行
Iterator< Map.Entry< Integer, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry< Integer, String> entry = iterator.next();
entry.getKey();
entry.getValue();
iterator.remove();
}
同样的在数据量小的时候,第二种和第三种速度差不多,第四种慢
在数据量在百万级和千万级的时候,第四种就很慢了,第三种最快,第二种次之
清晰直白,真不戳
基础送分题目不能丢
时隔几月再来看,还是没理解透彻