- 数据类型:
类型 | 大小 | 取值范围 |
---|---|---|
byte | 1字节 | -128~127 (10000000~01111111) [-2^7 ~ 2^7-1] |
short | 2字节 | -3276832767(100...011...) [-215~215-1] |
int | 4字节 | 根据以上规律可推出(正好超过20亿,无法表示全球人数) |
float | 4字节 | 根据以上规律可推出 |
long | 8字节 | 根据以上规律可推出(足够表示全球人数) |
double | 8字节 | 根据以上规律可推出 |
char | 2字节 | java采用Unicode,2个字节(16位)来表示一个字符 |
boolean | 未知 | true 、false |
二进制
原码:普通二进制数,第一位(8位)表示符号(1为负,0为正), 其余位表示值,eg:110。
正数情况:原码 = 反码 = 补码
负数情况:
反码:将原码按位取反得到的值成为原码的反码,eg:001。
补码:将反码+1,eg:010。
1.二进制转十进制的通用方法eg:
11101->12^4+123+1*22+02^1+12^0
2.正数二进制转十进制(位1比较多情况)eg:
01110 ---取反得到反码--->10001
---加1得到补码---->10010
---0比较多直接用通用方法-->-1*2^4 + 2 = -14
----取绝对值--------->14
3.负数二进制转十进制(位1比较多情况,负数二进制是补码形式)eg:
11110(补码)---减1得到反码---->11101(反码)
---取反得到原码---->00010(原码)
---0比较多直接用通用方法-->2
---取相反值------>-2-
浮点数值不适用于出现舍入误差的金融计算中。eg:
命令System.out.println(2.0-1.1) 得到的结果并不是想象中的0.9而是0.8999999999999.
原因是浮点数采用二进制表示,无法精确表示分数1/10就像十进制无法精确表示分数1/3一样.
如果需要在数值计算中保证精确的话,应使用BigDecimal类.
4.&、~的特殊用法:
Unicode编码表示为十六进制其范围从\u0000 到 \uffff。eg: \u03c0表示希腊字母π。
短路与、短路或 &&、|| 都是在第一个表达式如果能够确定值后就没必要执行第二个表达式了。
StringBuilder 适用单线程、StringBuffer允许多线程
数组拷贝 Arrays.copyOf()
两个相等的对象hashCode值一定相等
Equals与hashCode方法的定义必须一致:如果x.equals(y)返回true,那么x的hashCode()就必须与y的hashCode()相等。
eg:如果用定义的Employee.equals比较员工的ID,那么hashCode()就需要散列ID,而不是员工的姓名或存储地址。-
对象克隆
默认情况下是浅拷贝,也就是只拷贝普通成员变量和不可变的子对象(如String),它并不会拷贝其他可变对象。要想拷贝其他可变对象,
则需要实现深拷贝,即重写clone()方法。具体实现如下:
对于员工类:public class Employee implements Cloneable{//必须实现Cloneable接口
private String name;
private double salary;
private Date hireDay;public void Employee(String name,double salary,Date hireDay){ this.name = name; this.salary = salary; this.hireDay = hireDay; } public Employee clone() throws CloneNotSupportedException{ Employee cloned = (Employee)super.clone();//克隆了成员变量(salary)和String(name) cloned.hireDay = (Date)this.hireDay.clone();//克隆对象 return cloned; } } 调用方法如下: Employee employee = new Employee("neo",10000.0,new Date()); Employee cloned = employee.clone(); 这样employee 和 cloned 对象就互不相干了。
-
反射 -- 一切都是对象,类也是对象,而类是Class类的实例对象(类类型)
ArrayList list = new ArrayList();ArrayList
list1 = new ArrayList (); list1.add("hello"); //list1.add(20);错误的 Class c1 = list.getClass(); Class c2 = list1.getClass(); System.out.println(c1 == c2); //反射的操作都是编译之后的操作 /* * c1==c2结果返回true说明编译之后集合的泛型是去泛型化的 * Java中集合的泛型,是防止错误输入的,只在编译阶段有效, * 绕过编译就无效了 * 验证:我们可以通过方法的反射来操作,绕过编译 */ try { Method m = c2.getMethod("add", Object.class); m.invoke(list1, 20);//绕过编译操作就绕过了泛型 System.out.println(list1.size());//输出2 System.out.println(list1);//输出[hello,2] /*for (String string : list1) { System.out.println(string); }*///现在不能这样遍历 } catch (Exception e) { e.printStackTrace(); } } -
代理
--为某个对象提供一个代理,以控制对这个对象的访问(代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理)。分为以下两类: 静态代理:在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。 实现静态代理的步骤: 1.创建业务逻辑方法的接口 2.代理类和委托类都实现该接口 3.代理类拥有委托类的对象并通过构造方法传进来引用 4.代理类调用业务方法,可在方法前进行过滤,方法结束后进行处理 动态代理:代理类的源码是在运行期间通过反射生成的,代理类和委托类的关系是在程序运行时确定。 实现动态代理的步骤: 1.创建一个实现InvocationHandler接口的类,并实现invoke方法: public class TimeHandler implements InvocationHandler { private Object target; public TimeHandler(Object target) { super(); this.target = target; } /* * proxy 被代理对象 * method 被代理对象的方法 * args 方法的参数 * 返回值:Object 方法的返回值 * */ @Override public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { long starttime = System.currentTimeMillis(); System.out.println("汽车开始行驶...."); Object methodReturnType = method.invoke(target,args); long endtime = System.currentTimeMillis(); System.out.println("汽车结束行驶.... 汽车行驶时间:" + (endtime - starttime) + "毫秒!"); return methodReturnType; } } 2.创建被代理的类及接口: public interface Moveable { void move(); } public class Car implements Moveable { @Override public void move() { //实现开车 try { Thread.sleep(new Random().nextInt(1000)); System.out.println("汽车行驶中...."); } catch (InterruptedException e) { e.printStackTrace(); } } } 3.创建代理类:Proxy.newProxyInstance(ClassLoader classLoader,Class[] interfaces, InvocationHandler h);//classLoader为null,表示使用默认的类加载器。 Car car = new Car();//代理对象,委托类 InvocationHandler h = new TimeHandler(car); Class> cls = car.getClass(); /** * loader 类加载器 * interfaces 实现接口 * h InvocationHandler */ Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h); 4.通过代理调用方法: m.move(); Object sysIActivityManager = mInstanceField.get(gDefaultFieldObj);//系统的IActivityManager Class> iActivityManagerProxy = Class.forName("android.app.IActivityManager");//代理对象 Object proxy = Proxy.newProxyInstance(sysIActivityManager.getClass().getClassLoader(),new Class[]{iActivityManagerProxy}, new MyHandler(sysIActivityManager));
异常
异常分为RuntimeException(由程序错误导致的异常) 和 IOException(程序本身没问题,IO错误)。-
Hashmap
1.默认大小capacity=16 默认加载因子loadFactory=0.75,默认情况下,当buckets桶的数量
大于 capacity * loadFactory=12(即桶的数量为13时),进行resize,将capacity扩大到原来的2倍。
2.当我们在构造方法中传一个大小后,Hashmap会取大于参数大小的最小的2的次幂作为capacity。
比如我传一个15,则capacity=16.
原因:如果我们不以2的次幂作为capacity的话,比如capacity=15,当我们进行put操作时,源码如下:
上面的tab.length 其实就是capacity,index 就是得到table的索引,hash是经过hash()函数处理的hash值
当hash=8时:
index = 0100&1110(15-1的二进制)=0100
当hash=9时:
index = 0101&1110(15-1的二进制)=0100
我们发现,出现了hash碰撞,并且索引为9的数组(table[9])是无法存数据的(因为取9时被映射到了8),
而且不仅仅是索引为9无数据,而且只要是第四位为1(0001,0101,0111,1001,1101,1111等等)都无法存数据,造成了一半的空间浪费。
当capacity是2的次幂如16:
当hash=8时:
index = 0100&1111(16-1的二进制)=0100
当hash=9时:
index = 0101&1111(16-1的二进制)=0101
这样就避免了空间的浪费以及减少hash碰撞。 ArrayList
ArrayList 默认大小为10,每次扩容为原来大小的1.5倍
HashSet底层就是HashMap(数组加链表)、TreeSet底层就是TreeMap(红黑树)Math
Math.ceil()用作向上取整。 Math.floor()用作向下取整。 Math.round() 我们数学中常用到的四舍五入取整
Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行下取整
String
1 String str = "hello";
str = str + "world";
这两句话会造成内存泄漏;因为String是不可变对象,所以在内存中同时存在hello对象 和 helloworld对象,
但str引用指向的是helloworld对象,导致hello对象没有被引用,而且一直存在常量池中,从而导致内存泄漏
解决方案:StringBuilder
2 String str = "hello "+"world "+"are you ok?";
这句话会创建一个对象,因为JVM在编译阶段就确定了str的值为helloworldare you ok?
编译阶段还会进行宏替换eg:
final int len = 10;
String str = "java 的长度为 "+len;
以上也只会创建一个对象-
表达式
short s = 5; s = s - 2;上述代码会编译出错,因为2是int ,s-2也是int,不能直接赋给s。 但是 short s = 5; s-=2;上述代码不会出错,因为s-=2 等价于s = (short)(s-2) String str = "java.com.neo.lala"; String[] split = str.split(".");//这样得不到想要的结果(改为str.split("\\.")) 因为split方法的参数是正则表达式,而"."是匹配任何字符,所以需要转义
-
树
每个内部节点均为 m 度的有序树,称作 m 叉树 由 n 个节点构成的二叉树,高度至少为⎣log2n⎦ 不含 1 度节点的二叉树,称作真二叉树(Proper bina ry tree ),否则称作非真二叉树 (Improper binary tree)。
-
简单工厂模式(最少知识原则)
提供创建对象的功能,不需要关心具体的实现 使用场景:创建对象 好处:降低模块之间的耦合度 public class Factory { public static Api create(int type){ switch (type) { case 1: return new ImplA(); case 2: return new ImplB(); case 3: return new ImplC(); default: return new ImplC(); } } public
T creatProduct(Class clz) { Api api=null; try { api=(Api) Class.forName(clz.getName()).newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return (T)api; } } -
工厂方法模式(抽象工厂模式)
需求:导出数据(数据库文件、文本文件) 把对象的实现延迟到子类完成 Android使用场景:Activity生命周期(在子类加载具体的布局)、MediaPlayer的创建、ArrayList的迭代器 public interface IFactory {//抽象工厂 IApi createApi(); } public class AndroidFactory implements IFactory{//具体工厂 @Override public IApi createApi() { return new AndroidApi(); } } public class IOSFactory implements IFactory{//具体工厂 @Override public IApi createApi() { return new IOSApi(); } } 使用:IFactory factory=new IOSFactory(); factory.createApi();
-
单例模式
枚举(天生支持反序列化): 1 枚举中的属性必须放在最前面 2. 枚举中可以和java类一样定义方法 3. 枚举中的构造方法必须是私有的 public enum EnumManager { SDCardManager(10){ @Override public EnumManager getSingle() { return SDCardManager; } }, HttpManager(1) { @Override public EnumManager getSingle() { return null; } }; public SdCardImpl getSingleton() { return new SdCardImpl(); } public abstract EnumManager getSingle(); private EnumManager(int type){ } } EnumManager.SDCardManager.getSingleton(); DCL(Double Check Lock)存在的问题: public class DoubleCheckedLock { private static DoubleCheckedLock instance; public static DoubleCheckedLock getInstance() { if (instance == null) { //step1 synchronized (DoubleCheckedLock.class) { //step2 if(instance==null){ //step3 instance=new DoubleCheckedLock(); //step4 } } } return instance; } } Jdk5 以后支持处理器乱序执行 汇编指令(为了优化) 导致 指向地址和实例化堆区 顺序不同,eg: step4这一步分为两步(正常是先a后b、某些平台编译器会优化导致先b后a): a实例化DoubleCheckedLock堆区顺序不一样 b.instance 指向堆区地址 (优化情况先b后a) 比如线程A执行到step4的b处时(此时instance不为空),cpu让线程B执行,当到step1的时候发现instance不为空,就直接返回。 但事实上线程B得到的instance是没有实例化完成的导致错误
-
建造者模式
核心思想:构造方法私有,在Builder的构造方法里面初始化该类使用内部类(保持和房子(目标对象)一样的参数)来建造 public class Room { private String window; private String floor; private String doorl; private String chat; public void apply(WorkBuilder.RoomParmas parmas) { window=parmas.window; floor=parmas.floor; doorl=parmas.door; chat=parmas.chat; } } public class WorkBuilder{ private RoomParmas parmas; public WorkBuilder( ) { this.parmas = new RoomParmas(); } public WorkBuilder makeWindow(String window ) { parmas.window=window; return this; } public WorkBuilder makeFloor(String floorCorlor) { parmas.floor=floorCorlor; return this; } public WorkBuilder makeDoor(String door) { parmas.door=door; return this; } public Room makeChat(String chat) { parmas.chat=chat; return this; } public Room build() { Room room=new Room(); room.apply(parmas); return room; } class RoomParmas{ public String window; public String floor; public String door; public String chat; } }
-
原型模式
原型模式就是拷贝原型创建新对象(深拷贝) 目的:保护最原始的那一份存档。隐藏复制过程 使用场景:类初始化需要消耗很多资源、new 一个对象需要很多数据准备或访问权限、 一个对象需要提供给其他对象使用,但是可能会改变原型的值 项目中登录后保存session保存用户的登录信息、这些用户信息在其他地方用来做 登录校验,信息显示等。但是这些信息在其他地方是不允许修改的 public User getLoginUser(){ return loginUser.clone();/返回深拷贝 } 优点:原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是在循环体内产生大量对象时,原型模式可以更好的体现优点 缺点:直接在内存中拷贝,是不会调用其构造函数的。(注意!!)
-
策略模式
设置某一个策略就会拥有相应的功能(有一个上下文对象来设置某一个策略) eg:Android 中的插值器 代码中有大量if else 就可以考虑策略模式 public class Context { private IStrategy strategy; //构造函数,要你使用哪个策略 public Context(IStrategy strategy){ this.strategy = strategy; } public void setStrategy(IStrategy strategy){ this.strategy = strategy; } public void operate(){ this.strategy.operate(); } } public class FirstStrategy implements IStrategy { @Override public void operate() { System.out.println("first"); } } public class SecondStrategy implements IStrategy { @Override public void operate() { System.out.println("second"); } }
状态模式
和策略模式结构一样,但是行为跟状态有关(有一个上下文对象来控制具体的状态)
eg:电视遥控器在开机状态 的各种操作(调音量、换频道)才有效,在关机状态无效
实用场景:用户登录状态和未登录状态会有不一样的响应!!!!(非常实用)
public class PowerOn implements TVState{
@Override
public void nextChannel() {
System.out.println("下一频道");
}
@Override
public void preChannel() {
System.out.println("上一频道");
}
@Override
public void turnOn() {
System.out.println("正在开机");
}
@Override
public void turnOff() {
System.out.println("关机");
}
}
public class PowerOff implements TVState{
@Override
public void nextChannel() {}
@Override
public void preChannel() {}
@Override
public void turnOn() {
System.out.println("开机");
}
@Override
public void turnOff() {}
}
public class TvContext {
private TVState tvState=new PowerOff();
public void setTate(TVState tvState){
this.tvState=tvState;
}
public void turnOn(){
setTate(new PowerOn());
tvState.turnOn();
}
public void turnOff(){
setTate(new PowerOff());
tvState.turnOff();
}
public void nextChannel(){
tvState.nextChannel();
}
public void preChannel() {
tvState.preChannel();
}
}
-
责任链模式
当前处理者必须持有下一个处理者的引用 使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之前的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止 在安卓中的应用:事件传递机制、有序广播 优点:请求者和处理者关系解耦 缺点:需要遍历处理者,太多影响性能
-
命令模式
底层业务逻辑代码用接口进行隔离eg:登录时只关心登录成功和失败、登录具体逻辑应该使用接口隔离 客户端只是想要发出命令或者请求,不关心请求的真正接收者是谁,也不关心具体如何实现
-
模板方法模式
定义一个操作中的算法框架,而将一些步骤延迟到子类中,使得子类不改变算法的结构即可重复定义算法的某些特点步骤
-
观察者模式
被观察者一般会有一个列表来保存观察者的引用,并且提供增加和删除的方法,通知的时候会遍历这个列表,回调所有的观察者的方法
-
内存分配
了解内存分配的几种策略: 1.静态的 静态的存储区:内存在程序编译的时候就已经分配好,这块的内存在程序整个运行期间都一直存在。 它主要存放静态数据、全局的static数据和一些常量。 2.栈式的 在执行函数(方法)时,函数一些内部变量的存储都可以放在栈上面创建,函数执行结束的时候这些存储单元就会自动被释放掉。 栈内存包括分配的运算速度很快,因为内置在处理器的里面的。当然容量有限。 3.堆式的 也叫做动态内存分配。有时候可以用malloc或者new来申请分配一个内存。在C/C++可能需要自己负责释放(java里面直接依赖GC机制)。 在C/C++这里是可以自己掌控内存的,需要有很高的素养来解决内存的问题。java在这一块貌似程序员没有很好的方法自己去解决垃圾内存,需要的是编程的时候就要注意自己良好的编程习惯。 区别:堆是不连续的内存区域,堆空间比较灵活也特别大。 栈式一块连续的内存区域,大小是有操作系统觉决定的。 堆管理很麻烦,频繁地new/remove会造成大量的内存碎片,这样就会慢慢导致效率低下。 对于栈的话,他先进后出,进出完全不会产生碎片,运行效率高且稳定。 public class Main{ int a = 1;//堆内存 Student s = new Student();//堆内存 public void XXX(){ int b = 1;//栈里面 Student s2 = new Student();//s2一开始是在常量池里面,后面才压栈 } } 1.成员变量全部存储在堆中(包括基本数据类型,引用及引用的对象实体)---因为他们属于类,类对象最终还是要被new出来的。 2.局部变量的基本数据类型和引用存储于栈当中,引用的对象实体存储在堆中。-----因为他们属于方法当中的变量,生命周期会随着方法一起结束。 我们所讨论内存泄露,主要讨论堆内存,他存放的就是引用指向的对象实体。
-
线程
Java语言自己可以创建两种进程“用户线程”和“守护线程(Daemon)” 用户线程:就是我们平时创建的普通线程 守护线程:主要是用来服务用户线程. !!!当线程只剩下守护线程的时候,JVM就会退出.但是如果还有其他的任意一个用户线程还在,JVM就不会退出 将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点: (1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。 (2) 在Daemon线程中产生的新线程也是Daemon的。 (3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断 synchronized锁代码块锁的对象是同一个才会锁住eg: void method(String str){ synchronized(str){ ... } } method("xxx"); method(new String("xxx")); 上面两个方法同时执行时代码块是不会被锁住的,因为两个str虽然内容相同但是不是同一个对象。解决方法(字符串入池): synchronized(str.intern()){}
do{...}while(false)
do{
...
if (somethingIsWrong) break;
//more code
...
}while(false);
-
volatile
在线程中访问变量的时候,为了提高效率,会在线程中的私有空间中缓存这个变量。所以多个线程访问同一个变量的时候,会在各自线程的私有空间中缓存,对变量的修改是对这个缓存进行操作的。 但是从缓存更新到主存是需要时间的,所以多个线程对同一个变量进行操作这个变量的值就是不可靠的,使用volatile关键字,就会使得缓存的值会立即更新到主存,使得变量可见!
生产者消费者
public class ThreadTest1 {
//产品
static class ProductObject{
//线程操作变量可见
public volatile static String value;
}
//生产者线程
static class Producer extends Thread{
Object lock;
public Producer(Object lock) {
this.lock = lock;
}
@Override
public void run() {
//不断生产产品
while(true){
synchronized (lock) { //互斥锁
//产品还没有被消费,等待
if(ProductObject.value != null){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//产品已经消费完成,生产新的产品
ProductObject.value = "NO:"+System.currentTimeMillis();
System.out.println("生产产品:"+ProductObject.value);
lock.notify(); //生产完成,通知消费者消费
}
}
}
}
//消费者线程
static class Consumer extends Thread{
Object lock;
public Consumer(Object lock) {
this.lock = lock;
}
@Override
public void run() {
while(true){
synchronized (lock) {
//没有产品可以消费
if(ProductObject.value == null){
//等待,阻塞
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费产品:"+ProductObject.value);
ProductObject.value = null;
lock.notify(); //消费完成,通知生产者,继续生产
}
}
}
}
public static void main(String[] args) {
Object lock = new Object();
new Producer(lock).start();
new Consumer(lock).start();
}
}