基本数据类型
基本数据类型 | 封装类型 |
---|---|
byte 1字节 | Byte |
short 2字节 | Short |
int 4字节 | Integer |
long 8字节 | Long |
float 4字节 | Float |
double 8字节 | Double |
char 2字节 | char |
boolean 1字节 | Boolean |
(1)上面的第1个结论是说:boolean在底层实际会调用int,那么既然int占4个字节,boolean也自然占4个字节。即,boolean类型占4个字节。
(2)上面的第2个结论是说:boolean数组在底层会用到byte指令,那么既然byte占1个字节,boolean数组中的boolean也就占1个字节。即,boolean数组中的boolean占1个字节。
综上两点,得出最终结论:在符合JVM规范的虚拟机中,
如果boolean是单独使用:boolean占4个字节。
如果boolean是以“boolean数组”的形式使用:boolean占1个字节。
多态存在的三个必要条件:
继承,重写,父类引用指向子类对象。
接口和抽象类
相同点:
(1)都不能被实例化
(2)接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。
不同点:
(1)接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
(2)实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
(3)接口强调特定功能的实现,而抽象类强调所属关系
(4)接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。
内部类外部类
外部类访问内部类:如果内部类被static修饰,可以直接new内部类,内部类没有被static修饰,得先new外部类再new内部类
外部类访问内部类的对象必须建立内部类的对象。
内部类访问外部类:内部类可以直接访问外部类的成员包括私有成员。因为外部类持有内部类的引用。
内部类:常规内部类,静态内部类,局部内部类,匿名内部类。
常规内部类:常规内部类没有用static修饰且定义在在外部类类体中
局部内部类:在方法体或语句块(包括方法、构造方法、局部块或静态初始化块)内部定义的类成为局部内部类
匿名内部类:定义类的最终目的是创建一个类的实例,但是如果某个类的实例只是用一次,则可以将类的定义与类的创建,放到与一 起完成,或者说在定义类的同时就创建一个类以这种方法定义的没有名字的类成为匿名内部类
重载重写
重载发生在本类,特点是与返回值和修饰符无关,和方法名和参数列表相关。方法名相同,参数列表不同,访问修饰符和返回值类型可以相同也可以不同。
重写发生在父类子类之间,特点就是方法名相同,参数列表相同,但是具体的实现不同
参数列表必须相同
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
权限访问修饰符
作用域 | 当前类 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √/× | × |
default | √ | √ | × | × |
private | √ | × | × | × |
Protected子类与基类不在同一包中,子类只可以访问自己继承来的方法,不能访问另一包种基类的方法。
类加载器
根类加载器(Bootstrap)--C++写的,看不到源码;
启动类加载器BootstrapClassLoader
扩展类加载器(Extension)--加载位置:jre\lib\ext中;
扩展类加载器Extension ClassLoader
系统(应用)类加载器(System\App) --加载位置:classpath中;
系统类加载器App ClassLoader
自定义加载器(必须继承ClassLoader)。
类什么时候被初始化:
创建类的实例,也就是new一个对象
访问某个类或接口的静态变量,或者对该静态变量赋值
调用类的静态方法
反射(Class.forName("com.lyj.load"))
初始化一个类的子类(会首先初始化子类的父类)
JVM启动时标明的启动类,即文件名和类名相同的那个类只有这6中情况才会导致类的类的初始化。
双亲委派模型
如果一个类加载器收到了一个类加载请求,它不会自己去尝试加载这个类,而是把这个请求转交给父类加载器去完成。每一个层次的类加载器都是如此。因此所有的类加载请求都应该传递到最顶层的启动类加载器中,只有到父类加载器反馈自己无法完成这个加载请求(在它的搜索范围没有找到这个类)时,子类加载器才会尝试自己去加载。委派的好处就是避免有些类被重复加载。
面向对象6原则一法则
单一职责原则:一个类只做它该做的事情。
开闭原则:对扩展开放,对修改关闭。
依赖倒转原则:依赖于抽象而不依赖于具体
里氏替换原则:任何基类可以出现的地方,子类一定可以出现。
接口隔离原则:使用多个隔离的接口,比使用单个接口要好。
合成聚合复用原则: 尽量使用合成/聚合的方式,而不是使用继承。
迪米特法则:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立
设计模式
单例设计模式:单例对象的类只能允许一个实例。Java.LangRuntime典型的单例设计模式
享元模式:享元模式通过共享对象来避免创建太多的对象,String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子
装饰器设计模式(Decorator design pattern):允许向一个现有的对象添加新的功能,同时又不改变其结构。被用于多个Java IO类中
所谓饿汉式,就是直接创建出类的实例化;
而对于懒汉式,就是在需要的时候再创建类的实例化
ThreadLocal
和线程同步机制一样,都是为了解决多线程中相同变量的访问冲突问题,多个线程共享一个变量,ThreadLocal是为每一个线程创建一个单独的变量副本,每个线程都可以改变自己的变量副本而不影响其他线程所对应的副本
GC
Java执行GC判断对象是否存活基于两种方式:引用技术法,可达性分析算法。
GC的主要任务
分配内存
确保被引用对象的内存不被错误的回收
回收不再被引用的对象的内存空间
强引用(Strong Reference)
最普遍的引用,当内存不足时,宁可跑出OOM异常,垃圾回收器也不会回收它
软引用(Soft Reference)
内存足够,就不会回收它;但内存不够的时候,垃圾回收器就会回收它
实现内存敏感的高速缓存,比如网页缓存、图片缓存等
弱引用(Weak Reference)
生命周期更短.只要它在垃圾回收器扫描的区域中,被扫描到,就会被回收,不管内存是否足够.
虚引用(PhantomReference)
为了防止内存溢出,在处理一些占用内存比较大而且生命周期较长的对象时,可以尽量使用软引用和弱引用.
软引用适合用来进行缓存,当内存不够时能让JVM回收内存,弱引用能用来在回调函数中防止内存泄露。
集合
集合:对象的容器,实现了对对象进行操作的常用方法,可实现数组的功能
集合长度可变,存储引用类型。
数组:长度固定,存储引用类型,基本数据类型。
List集合:
有序,有下标,元素可以重复(ArrayList,LinkedList,Vector)
Set集合:
无序,无效标,元素不可重复(HashSet,TreeSet)
Map集合:
存储一对数据,无序,无下表,键不可重复,值可重复(HashMap,HashTable,TreeMap)
Collections:
集合工具类,定义了除了存取以外的集合常用方法。
Collection:
list:有序,有下标,可重复
Arraylist,Linkedlist,Vector
set:无序,无下标,不重复
HashSet->LinkedHashSet,SortedSet->TreeSet
Collection集合常用方法
public boolean add(E e): 把给定的对象添加到当前集合中 。
public void clear():清空集合中所有的元素。
public boolean remove(E e): 把给定的对象在当前集合中删除。
public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。
public boolean isEmpty(): 判断当前集合是否为空。
public int size(): 返回集合中元素的个数。
public Object[] toArray(): 把集合中的元素,存储到数组中
Collection
Collection collection = new Arraylist();
添加数据:
conllection.add();
collection.size();
collection.toString();
删除数据:
collection.remove();
collection.clear();
遍历:
Collection 没有索引,不能用for
增强for
for(Object objecct : collection)
迭代器
Iterator it = collection.iterator();
while(it.haseNext()){
it.next();
}
迭代器遍历集合不能使用Collection中的删除方法
List
List list = new ArrayList();
添加数据:
list.add();
删除数据:
list.remove();
遍历:
for()
forEach();
Iterator iterator = list.iterator();
列表迭代器,可以向前或向后遍历,添加修改删除元素。
ListIterator lit = list.ListIterator();
向前遍历
while(lit.hasNext()){
lit.nextIndex():
lit.next;
}
向后遍历:
while(lit.hasPrevious()){
lit.previousIndex();//查看下标
lit.prevoius()
}
ArrayList
Vector
遍历可以使用枚举器
Enueration en = vector.elements();
while(en.hasMoreElements()){
en.nextElement();
}
LinkedList
HashSet
存储结构:哈希表(数组,链表,红黑树)
基于HashCode实现元素不重复,
当存入元素的哈希码相同时,会调用equals进行确认,如果为true,则拒绝后者进入
HashSet hs = new HashSet();
添加数据:
hs.add();
TreeSet
存储结构:红黑树
基于排列顺序实现元素不重复
实现了SortedSet接口,对集合元素自动排序
元素对象的类型必须实现Comparable接口,指定排序规则
通过ComparableTo方法确定是否为重复元素,返回值为0,认定为重复元素
TreeSet ts = new TreeSet();
ts.add();
forEach遍历,Iterator遍历
Map集合
特点:存储键值对,键不能重复,值可以重复,无序。
Map
Map
添加元素:
map.put():
删除元素:
map.remove();
遍历:
keyset()方法;
//Set keyset = map.keyset();
for(String key :map.keySet()){
System.out.println(map.get(key));
}
entrySet()方法
//Set> entry = map.entrySet();
for(Map.entrySet entry : map.entrySet()){
System.out.println(entry.getValue());
}
HashMap
存储结构:哈希表
重复依据:键的hashcode()方法和equals方法
源码分析:
HashMap初始容量大小:16
数组最大容量:2^30
默认加载因子:0.75f
JDK1.8,当链表长度大于8时,调整成红黑树
JDK1.8,当链表长度小于6时,调整成链表
JDK1.8,当链表长度大于8时,并且集合元素个数大于等于64时,调整成红黑树。
TreeMap
HashTable
Colltecitons工具类
集合工具类,,定义了除存取以外的集合常用方法
泛型集合
泛型就是参数化类型, 把类型当作参数一样传递。
编译时的一种类型,约束类中的方法,属性,运行时无效。
特点:编译时即可检查,而非运行时抛出异常(ClasscastException)
访问时,不必类型转换(拆箱)
不同泛型之间不能相互赋值,泛型不存在多态
提高代码的重用性,防止类型转换异常,提高代码的安全性
泛型的类型擦除
java中泛型在编译期间,所有的泛型信息都会被擦掉。
擦除规则:
若泛型类型没有指定具体类型,用Object作为原始类型
若有限定类型< T exnteds XClass >,使用XClass作为原始类型;
若有多个限定< T exnteds XClass1 & XClass2 >,使用第一个边界类型XClass1作为原始类型
可以通过反射添加其他类型元素
public class Test {
public static void main(String[] args) throws Exception {
ArrayList list = new ArrayList();
list.add(1);
list.getClass().getMethod("add", Object.class).invoke(list, "asd");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
泛型应用场景
写框架,写工具类,写通用方法
泛型应用类型
泛型接口:
访问权限 +interface +接口名称 + <泛型标示>{}
1.1
interface Hello{
public gener();
}
class HelloImpl implements Hello{}
1.2
interface Hello{
public gene();
}
class HelloImpl implements Hello{}
泛型类:
访问修饰符+类名+泛型标示
泛型方法:
访问权限 +<泛型标示>+泛型标示 方法名称(泛型标示 参数名称)
泛型数组
上限通配符
使用关键字 extends 实现
实例化时,指定类型实参只能是extends后类型的子类或其本身。
List extends Hello>
下限通配符
使用关键字 super 实现
实例化时,指定类型实参只能是extends后类型的子类或其本身。
List super Hello>
多线程
进程:系统中正在运行的一个应用程序;程序的一次启动执行过程。
线程:线程是进程创建的一个实体。
并发(Concurrency):同一时刻多个任务交替执行。单核cpu实现的多任务就是并发。
并行(Parallelism):同一时刻多个任务同时执行。多核cpu可以实现并行。
串行:同一时刻多个任务顺序执行。
实现多线程的方式:
1.继承Thread类
public class Hello extends Thread{
@Override
public void run(){
System.out.println("Thread类实现了Runable接口的run方法")
}
}
.不能多次调用Thread中的start()方法,否则会抛出IllegalThreadStateException异常。
.执行start()方法的顺序不代表线程启动的顺序,即并不是说,越早调用某个线程的start()方法,它就能越早的执行其中的run()方法
2.实现Runable接口
public class ThreadRl{
public static void main(String[] args){
Hello hello = new Hello();
Thread thread = new ThreaProxy(hello);
thread.start();
}
}
pulic class Hello implements Runable{
private Runable target = null;
@override
public void run(){
System.ou.println("真正实现多线程调用的是start0方法")
}
public void run(){
if(target !=null){
target.run();
}
}
public ThreadProxy(Runable target){
this.target = target;
}
public void start(){
start0();
}
public void start0(){
run();
}
}
3.实现callable,重写call方法
public class MyThread implements Callable{
@override
String call() throws Exception(){
return "implements callable"
}
继承Threa与实现Runable的区别:
实现Runabel接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Ruanable
Callable与Runable的区别:
Callable可以在任务结束后提供一个返回值,Runnable没有这个功能
Callable中的call()方法可以抛出异常,而Runnable的run()方法不能抛出异常
线程常用方法:
public void satrt() 启动线程
public final void setName(String name) 改变线程名称,使之与参数 name 相同
public final void setPriority(int piority) 更改线程的优先级
public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程
public final void join(long millisec) 等待该线程终止的时间最长为 millis 毫秒
public void interrupt() 中断线程
public static void static yield() 暂停当前正在执行的线程对象,并执行其他线程
yield,礼让线程,礼让时间不确定,不一定成功
public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠
public static Thread currentThread() 返回对当前正在执行的线程对象的引用
线程状态:
1.NEW 尚未启动的线程-->start()
2.RUNABLE 在Java虚拟机种执行的线程
2.1.Ready
2.2.Running
3.BLOCKED 被阻塞等待监视锁定的线程 (等待进入同步代码块的锁)
4.WATING 正在等待另一个线程执行特定动作的线程 (wait(),join(),lockSupport.park())
5.TIMED-WATING 正在等待另一个线程执行动作达到指定等待时间
6.TERMINATED 已退出的线程
线程锁:
互斥锁
Java中引入对象互斥锁的概念来保证共享数据操作的完整性
一个线程获得资源的使用权后就会将该资源加锁,使用完后会将其解锁
Synchronized修饰互斥锁
同步的局限性:导致程序的执行效率低
同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
同步方法(静态的)的锁为当前类本身
释放锁
当前线程的同步方法 同步代码块执行结束
当前线程在同步代码块 同步方法中遇到break,return
当前线程在同步代码块 同步方法中出现了未处理的 Error 或 Exception 导致异常结束
wait() 释放锁;sleep()不释放锁;suspend()线程挂起,不释放锁;yield()暂停当前线程,不释放锁
线程同步机制
当有一个线程在对内存进行操作时,其他线程不可以对这个内存地址进行操作
线程死锁
死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
线程死锁的四个条件:
互斥,占有并等待,非抢占,循环等待
避免死锁的方法:
加锁顺序;死锁检测;加锁时限
悲观锁
共享锁(shared locks)又称为读锁,简称S锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改
排他锁(exclusive locks)又称为写锁,简称X锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数
wait()释放锁
sleep()不释放锁
yield()暂停当前线程,不释放锁。
守护线程
为工作线程提供服务,垃圾回收机制就是守护线程。
interruped中断线程
I/O
内存与存储设备之间传输数据的通道。
流的分类:
按单位:
字节流:以字节为单位,可以读写所有数据
字符流:以字符为单位,只能读写文本数据
按功能:
节点流:具有实际传输数据的读写功能。
过滤流:在节点流的基础上增强功能。
文件字节流:
FileInputStream
public int read(byte[] b)
FileOutPutStream
public void write(byte[] b)
字节缓冲流:
缓冲流:BufferedInputStream/BufferedOutputtream
提高IO效率,减少访问磁盘的次数
数据存储在缓冲区中,flush是将缓存区的内容写入文件中,也可以直接close
对象流
ObjectInputStream/ObjectInputStream
增强了缓冲区功能
增强了读写8中基本数据类型和字符串功能。
增强了读写对象的功能---{1. readObejct()从流中读取一个对象 2.writeObject(Object obj)向流中写入一个对象
使用流传输对象的过程称为序列化,反序列化
序列化必须要实现Serializable接口
序列化类中对象属性要求实现serializable接口
序列化版本号ID,保证序列化的类和反序列化的类是同一个类
使用transient修饰属性,这个属性不能序列化
静态属性不能序列化。
字符编码
ISO-8859-1 收录除ASCII外,还包括西欧,希腊语等对应的文字符号
UTF-8针对Unicode编码表的可变长度字符编码
GB2312 简体中文
GBK 简体中文,扩充
BIG5 繁体中文
字符流
字符流的父类(抽象类)
Reader:字符输入流
1 public int read(){}
2 public int read(char[]){}
3 public int read(char[]){}
Writer:字符输出流
1 public void writer(int n){}
2 public void writer(String str){}
3 public void writer(char[] c){}
文件字符流
FileReader:
public int read(char[] c)
FileWrite:
public void write(String str){}
字符缓冲流
缓冲流:BufferedReader/BufferecWriter
高效读写
支持输入换行符
可一次写一行,读一行
读取:read(), readLine()一行一行的读取
写入:write(), newLine()写入一个换行符
转换流
桥转换流:
InputStreamReader/InputStreamWriter
可将字节流转换为字符流
可设置字符的编码方式
反射
1 反射是指Java运行状态中
任何一个类都能知道当前类的属性和方法
任何一个对象都能调用当前对象的属性和方法
2 反射是在运行状态下,可以对任何一个类获取,并可以操作这个类的名称,类中的普通方法,构造方法,字段属性等信息
反射的应用
- 加载JDBC驱动:Class.forName("com.cj.mysql.driver")
- idea开发工具的提示功能
- 架构 必须使用反射 例如:Spring IOC DI 需用反射
反射的优缺点
优点:
可以在运行期间拿到我们想要的任何类中的数据
可以优化多出重复的工作
缺点:
效率慢
无论要获取字节码文件中的类名、普通方法、字段名、构造方法,必须先获得这个字节码文件
1. 在代码编写阶段获取其class对象
Class cls = Class.forName("com.cy.Person");
2. 在字节码阶段获取其class对象
Class ps = Person.class;
3. 虚拟机运行阶段获取其class对象
Person p = new Person();
Class as = p.getClass();
Java文件编译为字节码文件的时候,只会进行一次的字节码文件初始化编写。
Field[] fields = cls.getFields();//获取公共的字段属性
Field sm = cls.getField("sm");//获取单个指定属性
sm.set(cls.newInstance,"xiaozhi");//Person p1 = new Person();p.setSm("xiaozhi")
System.out.println(sm.get(cls.newInstance));//Person p1 = new Person();p1.getSm();
Field[] fs = cls.getDeclaredFields();//获取所有的属性(公共的,私有的)
cls.newInstance();//通过字节码Class对象获取到一个Object实例对象
cls.newInstance() 实质是调用默认的构造方法实例化一个对象
异常
Java种的所有异常都来自顶级父类Throwable
Throwable下有两个子类Exception和Error
Error是程序无法处理的错误,一旦出现这个错误,则程序被迫停止运行
Exception不会导致程序停止,又分为两个部分RuntimeException运行时异常和CheckedException检查异常
RuntimeException常常发生在程序运行过程中,会导致程序当前线程执行失败,CheckedException常常发生在程序编译过程中,会导致程序编译不通过。
Java面试问题
多线程,并发及线程基础
数据类型转换的基本原则
垃圾回收
Java集合框架
数组
I/O
泛型
JVM
数据结构和算法
异步