所有线程的共享数据区:
方法区:存储每一个类的结构信息 运行时常量池
堆区:最大 大部分类实例、对象、数组
每一个线程的一块私有数据区:
虚拟机栈:存储局部变量、操作数栈、动态链接、方法返回地址 为执行java方法服务
本地方法栈:功能与虚拟机栈类似,为native方法服务
pc寄存器:存放当前正在执行的字节码指令的地址
类加载检查
分配内存
2.1 内存分配的方式:
指针碰撞:要求内存规整 标记-整理
空闲列表: 堆内存不规整情况 标记-清除
2.2 内存分配并发:
CAS+失败重试
TLAB:预先在 Eden 区分配内存,首先在 TLAB 分配,当对象大于 TLAB 中的剩余内存或 TLAB 的内存已用尽时,再采用上述的 CAS 进行内存分配
初始化零值
设置对象头 类的元数据信息、对象的哈希码、对象的 GC 分代年龄
执行 init 方法
对象包含对象头,实例数据,对齐填充
只要使用new方法,便需要创建新的对象
String
直接使用双引号声明出来的 String 对象会直接存储在常量池中。
使用new创建的字符串对象在堆内存分配。
String str3 = str1 + str2 会在堆内存上创建新的对象 String str4 = “string” 存储在常量池中
System.out.println(str3 == str4);//false
1加载-2验证-3准备(内存初始化)-4解析(将常量池的符号引用替换为直接引用,符号引用是用一组符号来描述所引用的目标,直接引用是指向目标的指针)-5初始化(执行类构造器、类变量赋值、静态语句块)
新生代:
进入条件:优先选择在新生代的Eden区被分配
老年代:
进入条件:
大对象经过第一次MinorGC仍存在,能被Survivor容纳
如果Survivor中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象进入老年代
如果Survivor空间无法容纳新生代中Minor GC之后还存活的对象
对象优先在eden区分配、大对象直接进入老年代、长期存活的对象将进入老年代
对象计数器:对象在 Survivor 中每熬过一次 MinorGC,年龄就增加 1 岁。当年龄增加到一定程度(15岁),晋升到老年代
引用计数器 回收对象:每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的,表示对象已死亡
可达性分析 回收对象:通过一系列的GC Roots的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时则此对象是不可用的。
如何回收:新生代使用复制算法,老年代使用标记-清理或者标记-整理
复制算法:将可用内存按容量划分为Eden、from survivor、to survivor,分配的时候使用Eden和一个survivor,Minor GC后将存活的对象复制到另一个survivor,然后将原来已使用的内存一次清理掉。这样没有内存碎片。
标记-清除:首先标记出所有需要回收的对象,标记完成后统一回收被标记的对象。会产生大量碎片,导致无法分配大对象从而导致频繁GC。
标记-整理:首先标记出所有需要回收的对象,让所有存活的对象向一端移动。
分代收集:新生代使用复制算法,老年代使用标记-清除或标记-整理算法
新生代GC(Minor GC)发起条件:当Eden区不足以继续分配对象
老年代GC(Full GC)条件:
1、调用System.gc
2、老年代空间不足
3、方法区空间不足
并发:有可能交替执行
串行收集器:一个线程,慢 并行收集器:可控的吞吐量,最大停顿时间(复制,标记-整理)
并发收集器:以最短停顿时间为目标 精确控制停顿时间且垃圾回收效率最高
CMS针对老年代,有初始标记、并发标记、重新标记、并发清除四个过程,标记阶段会Stop The World,使用标记-清除算法,所以会产生内存碎片。
Serial 收集器:串行 新生代使用复制算法,老年代使用标记-整理算法
ParNew 收集器: Serial的多线程版本
Parallel Scavenge 收集器:复制算法的多线程收集器 吞吐量(高效率的利用 CPU) 提供很多参数供用户找到最合适的停顿时间或最大吞吐量
CMS(Concurrent Mark Sweep)收集器:一种以获取最短回收停顿时间为目标的收集器。 并发,让垃圾收集和用户线程同时工作
G1收集器:面向服务器 并行与并发 分代收集
重排序可能会导致多线程程序出现内存可见性问题。
new Integer(123) 与 Integer.valueOf(123) 的区别在于:
编译器会在自动装箱过程中调用valueOf方法,多个值相同且值在缓存池范围内的 Integer 实例使用自动装箱来创建,会引用相同对象
Integer m = 123;
Integer n = 123;
System.out.println(m == n); // true
不可变:String参数不可变,线程安全
字符串常量池:保存字符串字面量
String s = new String(“abc”):一共会创建两个字符串对象(前提是 String Pool 中还没有 “abc” 字符串对象)。
抽象类:abstract 抽象类不能被实例化,需要继承抽象类才能实例化其子类
接口:接口的字段默认都是 static 和 final 的。接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。
数组结构,新建一个hashmap,初始化一个数组 transient Entry[] table;数组中的元素持有指向下一个元素的引用,构成链表。
当我们往hashmap中put元素的时候,先根据key的hash值得到这个元素在数组中的位置(即下标),然后就可以把这个元素放到对应的位置中了。如果这个元素所在的位子上已经存放有其他元素了,那么在同一个位子上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。从hashmap中get元素时,首先计算key的hashcode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素
public V put(K key, V value){
return putVal(hash(Key),key,value, false, true);
}
static final int hash(Object key){
int h;
return (key == null?)0:(h= key.hashCode() ^ (h>>>16))
}
将其不带符号的右移16位,再与key的hashcode做异或运算
Collections.sort(arrayList, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* TODO重写compareTo方法实现按年龄来排序
*/
@Override
public int compareTo(Person o) {
if (this.age > o.getAge()) {
return 1;
} else if (this.age < o.getAge()) {
return -1;
}
return age;
}
}
参数化类型 定义时使用参数化,使用时传入具体类型
只在编译阶段有效
泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
public class GenericClass {
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public static class Generic<T>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
public static class Service{
public int a;
public float b;
public String c;
public Service(int a, float b, String c) {
this.a = a;
this.b = b;
this.c = c;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
}
public static void main(String[]args){
Generic<Integer> a = new Generic<Integer>(new Integer(2));
Generic<String> b = new Generic<String>("2");
System.out.println(a.getKey()+" "+b.getKey());
Generic g1 = new Generic(1);
Generic g2 = new Generic("1");
Generic g3 = new Generic(3.44);
Service service= new Service(1,2,"对象");
Generic g4 = new Generic(service);
System.out.println(g1.getKey()+" "+g2.getKey()+" "+g3.getKey()+" "+((Service)g4.getKey()).getC());
}
}
public interface Generator<T> {
public T next();
}
public class AnimalGenerator implements Generator<String>{
private String[]animals = new String[]{"cat","dog"};
@override
public String next(){
Random rand = new Random();
return animals[rand.nextInt[3]];
}
}
一般使用?代替具体的类型实参,不是类型形参,可以吧?看成所有类型的父类
当具体类型不确定时使用
Generic<Integer> gg = new Generic<>(3);
Generic<Number> nn = new Generic<>(4);
showValue(gg);
showValue(nn);
public static void showValue(Generic<?>obj){
System.out.println(obj.getKey());
}
泛型类在实例化类时指明泛型的具体类型;泛型方法在调用方法时指明泛型的具体类型。
public <T> T genericMethod(){}
public <T> void show_T(T t){
Systerm.out.println(t.toString());
}
如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法:
public class StaticGenerator<T>{
public static <T> void show(T t){
}
}
//定义
List<String>[] ls1 = new ArrayList[10];
List<?>[] ls = new ArrayList<?>[10];
List<Integer> integers= new ArrayList<>();
integers.add(11);
ls[0] = integers;
System.out.println(ls[0].get(0));
Integer i = (Integer) ls[0].get(0); //需要做显式的类型转换
String i = (String) ls[0].get(0); //Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
运行状态,对于任意一个类(对象)都能知道(调用)这个类的所有属性和方法动态获取信息以及动态调用对象方法的功能
遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。
public class fanshe {
public static class Demo{
public int a;
public int b;
}
public static void main(String[]args){
Demo demo = new Demo();
// 第一种方式
Class demoClass = demo.getClass();
System.out.println(demoClass.getName());
//第二种方式
Class demoClass1 = Demo.class;
System.out.println(demoClass1.getName() == demoClass.getName());
// 第三种方式
try {
Class demoClass2 = Class.forName("interview.fanshe$Demo");
System.out.println(demoClass2.getName() == demoClass1.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public static class Demo{
public int a;
public int b;
public Demo(){}
private Demo(int a){
this.a=a;
}
public Demo(int a,int b){
this.a = a;
this.b = b;
}
}
import java.lang.reflect.Constructor;
Constructor[] array = demoClass2.getConstructors();//
System.out.println("获取所有公有构造方法"+array);
Constructor[] array1 = demoClass2.getDeclaredConstructors();//获取所有的构造方法(包括:私有、受保护、默认、公有)
System.out.println("获取所有的构造方法(包括:私有、受保护、默认、公有)"+array1);
try {
Constructor array2 = demoClass2.getConstructor(null);//获取公有、无参的构造方法
System.out.println("获取公有、无参的构造方法"+array2);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
Constructor array3 = demoClass2.getDeclaredConstructor(int.class);
System.out.println("获取私有构造方法"+array3);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
获取成员变量并调用
import java.lang.reflect.Field;
try {
Object obj = demoClass2.getConstructor().newInstance();
Field f = demoClass2.getField("a");
f.set(obj,1);
Demo demo0 = (Demo) obj;
System.out.println(demo0.a);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
Field[] fields = demoClass2.getFields();//获取所有公有的字段
for(Field f : fields){
System.out.println(f);
}
fields = stuClass.getDeclaredFields();//获取所有的字段(包括私有、受保护、默认的)
public static class Demo{
public int a;
public int b;
public Demo(){}
private Demo(int a){
this.a=a;
}
public Demo(int a,int b){
this.a = a;
this.b = b;
}
public void show1(String s){
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
private String show2(int age){
System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = \" + "+age);
return "abcd";
}
}
import java.lang.reflect.Method;
Method[] methodArray = stuClass.getMethods();
Method methods[] = demoClass2.getMethods();//获取所有的”公有“方法
for(Method m : methods){
System.out.println(m);
}
try{
methods = demoClass2.getDeclaredMethods();//获取所有的方法,包括私有的
Method m = demoClass2.getMethod("show1", String.class);//获取公有的show1()方法*
System.out.println(m);
Method m1 = demoClass2.getDeclaredMethod("show2", int.class);//获取私有的show2()方法*
m1.setAccessible(true);//解除私有限定
Object obj = demoClass2.getConstructor().newInstance();
Object result = m1.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result);
}catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
import java.lang.reflect.Method;
try{
Class demoClass = Class.forName("interview.fanshe$Demo");
Method methodMain = demoClass .getMethod("main", String[].class);
//3、调用main方法
// methodMain.invoke(null, new String[]{"a","b","c"});
//第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
//这里拆的时候将 new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
}catch (Exception e) {
e.printStackTrace();
}
public class Demo{
public void show(){
System.out.println("this is show mehod");
}
}
配置文件pro.txt内容;
className = interview.fanshe$Demo
methodName = show
反射执行
public static void main(String[] args) throws Exception {
//通过反射获取Class对象
Class demoClass = Class.forName(getValue("className"));
//2获取show()方法
Method m = demoClass .getMethod(getValue("methodName"));
//3.调用show()方法
m.invoke(demoClass .getConstructor().newInstance());
}
public static String getValue(String key) throws IOException{
Properties pro = new Properties();
FileReader in = new FileReader("pro.txt")
pro.load(in);
in.close();
return pro.getProperty(key);
}
状态:新建-就绪-运行-阻塞-就绪-运行-死亡 start() wait() notify() notifyall()
实现:Thread Runnable Callable
线程池的作用:线程创建和销毁的开销大,放回线程池有效利用
线程间通信的方式:
定义: 不同线程资源竞争下分配不同线程执行方式的同步工具 获得锁才能访问同步代码
保证synchronized修饰的方法和代码块任意时刻只能一个线程执行
原理:
synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令, monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置;锁计数器用于锁处于释放(0)还是被占有状态,可重入,一个线程可以占有多次
synchronized 修饰方法是通过ACC_SYNCHRONIZED 标识,指明方法是同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。
synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。
synchronized 关键字加到实例方法上是给对象实例上锁。
双重校验锁实现对象单例(线程安全):
public class Singleton{
private volatile static Singleton uniqueInstance;
private Singleton(){}
private static Singleton getUniqueInstance(){
if(uniqueInstance == null){
synchronized(Singleton.class){
uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
}
与synchronized相同的语义 必须手动释放锁
性能:资源竞争激烈情况下,lock性能比synchronized好
用法:synchronized可以用在代码块,方法上。lock通过代码实现,需手动释放
原理:synchronized monitorenter monitorexit lock 使用AQS在代码级别实现
直接与主内存交互,读写保证可见性
禁止JVM进行指令重排序,保证数据可见性,但不能保证原子性
volatile用于解决变量在多个线程之间的可见性,synchronized用于解决多个线程之间访问资源的同步性
功能:为每一个线程创建一个专属本地变量存放空间,存取线程的私有数据。
ThreadLocalMap存储结构:ThreadLocal->变量
可以通过Thread.currentThread()获取到当前线程对象后,直接通过getMap(Thread t)可以访问到该线程的ThreadLocalMap对象。每个Thread中都具备一个ThreadLocalMap,而ThreadLocalMap可以存储以ThreadLocal为key的键值对,value 就是 ThreadLocal 对象调用set方法设置的值。
ThreadLocal 内存泄漏:key为弱引用,value是强引用,GC只回收了key,value无法回收,导致内存泄漏;解决方法是在使用王ThreadLocalMap之后手动调用其remove方法,会删除key为null的记录。
锁粗化:某些情况下把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗。
for(int i=0;i<size;i++){
synchronized(lock){
}
}
synchronized(lock){
for(int i=0;i<size;i++){
}
}
锁消除:发生在编译器级别的一种锁优化方式
即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行削除,判定依据来源于逃逸分析的数据支持,如果判断到一段代码中,在堆上的所有数据都不会逃逸出去被其他线程访问到,那就可以把它们当作栈上数据对待,认为它们是线程私有的,同步加锁自然就无须进行。
ThreadPoolExecutor的构造方法实现
通过Executor 框架的工具类Executors来实现
线程池的创建及注意事项
信号量Semaphore:阻塞线程且能控制统一时间请求的并发量的工具
CyclicBarrier:可以让一组线程相互等待,当每个线程都准备好之后,所有线程才继续执行的工具类
Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。
使用在包java.net中提供了两个类Socket和ServerSocket
选择端口号时,最好选择一个大于1023的数以防止发生冲突
BIO:InputStream OutputStream Reader Writer 同步阻塞模型
NIO:Channel Buffer Selector IO多路复用的同步非阻塞模型
同步非阻塞:进程先将一个套接字在内核中设置成非阻塞再等待数据准备好,在这个过程中反复轮询内核数据是否准备好,准备好之后最后处理数据返回
AIO:属于事件和回调机制的异步非阻塞模型
不可中断的操作,不受其他线程干扰,原子类都在Java.util.concurrent包里
基本数据类型:AtomicInteger AtomicLong AtomicBoolean
数组类型:AtomicIntegerArray AtomicLongArray AtomicReferenceArray(引用类型数组)
引用类型:AtomicReference AtomicStampedReference:原子更新引用类型里的字段原子类 AtomicMarkableReference :原子更新带有标记位的引用类型
public final int get() //获取当前的值
public final int getAndSet(int newValue)//获取当前的值,并设置新的值
public final int getAndIncrement()//获取当前的值,并自增
public final int getAndDecrement() //获取当前的值,并自减
public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
多线程环境不使用原子类保证线程安全(基本数据类型):
class Test {
private volatile int count = 0;
//若要线程安全执行执行count++,需要加锁
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
多线程环境使用原子类保证线程安全(基本数据类型)
class Test2 {
private AtomicInteger count = new AtomicInteger();
public void increment() {
count.incrementAndGet();
}
//使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
public int getCount() {
return count.get();
}
}
原理
主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。
public final int get(int i) //获取 index=i 位置元素的值
public final int getAndSet(int i, int newValue)//返回 index=i 位置的当前的值,并将其设置为新值:newValue
public final int getAndIncrement(int i)//获取 index=i 位置元素的值,并让该位置的元素自增
public final int getAndDecrement(int i) //获取 index=i 位置元素的值,并让该位置的元素自减
public final int getAndAdd(int delta) //获取 index=i 位置元素的值,并加上预期的值
boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将 index=i 位置的元素值设置为输入值(update)
public final void lazySet(int i, int newValue)//最终 将index=i 位置的元素设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
用于构建锁和同步器的框架
原理:如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH队列:虚拟的双向队列(不存在队列实例,仅存在结点之间的关联关系),AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。
程序计数器私有主要是为了线程切换后能恢复到正确的执行位置。
为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。
堆和方法区共享,堆用于存放对象,方法区用于存放类信息和常量
问:并发和并行的区别:
并发:同一时间段,多个任务都在执行 (单位时间内不一定同时执行);
并行:单位时间多个任务同时执行
问:sleep()和wait()的区别
答:sleep()没有释放锁,wait()释放锁;wait()后需要notify()唤醒线程,sleep()则会自动苏醒;
问:为什么不直接执行run()方法,需要执行start方法?
答:调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。
Spring是个包含一系列功能的合集,如快速开发的Spring Boot,支持微服务的Spring Cloud,支持认证与鉴权的Spring Security,Web框架Spring MVC。IOC与AOP依然是核心。
模块:核心容器、数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块
Core组件是核心,Bean组件和Context组件是实现IOC和依赖注入的基础,AOP组件用来实现面向切面编程。
1、发送请求 DispatcherServlet拦截器拿到交给HandlerMapping
2、依次调用配置的拦截器,最后找到配置好的业务代码Handler并执行业务方法
3、包装成ModelAndView返回给ViewResolver解析器渲染页面
Bean实例化-值和bean的引用注入到Bean对应的属性-把容器信息注入BeanBe-Bean处理-Bean初始化和销毁
控制反转:原先是自己主动创建,现在是容器工具创建实例, 面向接口编程和配置文件减少对象之间的耦合
依赖注入:在运行过程中,当你需要这个对象是才给你实例化并注入
IOC容器是一个Map,存放的是各种对象
面向切面编程 是OO的补充 OOP从上往下 会有重复代码 AOP将和业务无关的重复代码抽取出来 比如权限管理、日志、事务管理 降低耦合度,减少重复代码
实现AOP的方式:
单例bean线程安全:在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中。
@Component:作用于类 通过类路径扫描来自动侦测以及自动装配到Spring容器中
@Bean:作用于方法 告诉了Spring这是某个类的示例,当我需要用它的时候还给我
@Bean 注解比 Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
上面的代码相当于下面的 xml 配置
ORM持久化框架,支持普通SQL查询,存储过程以及高级映射,通过简单的xml或注解用于配置和原始映射,将接口和对象映射成数据库中记录。
InnoDB支持事务 支持外键 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表
InnoDB有行级锁,MyISAM是表级锁
选择MyISAM:系统插入和查询多,不需要事务外键
1、优化SQL语句和索引 在where、groupby、orderby用到的字段上建立索引
2、加缓存
3、主从复制,读写分离(主库负责写,从库负责读)
4、垂直拆分 数据表列的拆分
5、水平切分 数据表行的拆分
SQL优化:
1、对经常查询的列建立索引
2、查询使用精确列名
3、减少子查询,使用join
4、不用not in(使用全表扫描);不用is null not is null(会使索引和索引统计更加复杂)
在某列上建立索引,下次查询时会使用索引查询 最左前缀匹配原则
当表特别大 select * from table where name = xxx and age = xxx 优化策略:创建复合索引将最常用作限制条件的列放在最左边,依次递减。其次还要考虑该列的数据离散程度,如果有很多不同的值的话建议放在左边,name的离散程度也大于age。
一组操作,要么执行,要么不执行
ACID:原子性,一致性,隔离性,持续性
行锁相对于表锁来说,优势在于高并发场景下表现更突出
表的大部分数据需要被修改,或者是多表复杂关联查询时,建议使用表锁优于行锁
悲观锁:select for update Synchronized 适合多写的场景
乐观锁:先查询一次数据,然后使用查询出来的数据+1进行更新数据,如果失败则循环 适合读多写少
实现方式:
CAS和synchronized的使用场景:CAS适合写比较少的场景,synchronized适合写比较多的场景
SQL语句在MySQL中如何执行的
加快检索的速度 索引需占物理空间 创建和维护索引耗费时间
类型:BTree索引和哈希索引
原理:使用B+树 一个节点可以存储多个索引的key,一次磁盘IO可以读取到很多key且叶子节点之间还加上了下一个叶子节点的指针
索引优化策略:
水平分表:为了解决单标数据量过大(数据量达到千万级别)问题。所以将固定的ID hash之后mod,取若0~N个值,然后将数据划分到不同表中,需要在写入与查询的时候进行ID的路由与统计
垂直分表:为了解决表的宽度问题,同时还能分别优化每张单表的处理能力。所以将表结构根据数据的活跃度拆分成多个表,把不常用的字段单独放到一个表、把大字段单独放到一个表、把经常使用的字段放到一个表
分库:面对高并发的读写访问,当数据库无法承载写操作压力时,不管如何扩展slave服务器,此时都没有意义了。因此数据库进行拆分,从而提高数据库写入能力,这就是分库。
数据库和表的字符集统一使用 UTF8
所有表和字段都需要添加注释
禁止在数据库中存储图片,文件等大的二进制数据
String sql = "insert into t select ?,?";
PreparedStatement statement = con.prepareStatement(sql);
statement.setInt(1, 123456);
statement.setString(2, "abc");
statement.executeUpdate();
statement.close();
select ‘column1’,‘column2’,‘column3’ from table limit num;
select ‘column1’,‘column2’,‘column3’ from table limit num1, num2;
select distinct 'column' from table;
select distinct 'column1','column2' from table;
select ‘column1’,‘column2’,‘column3’
from table
order by 'indexColumn' desc
limit num;
select ‘column1’,‘column2’,‘column3’
from table
where 'indexColumn' = value
limit num;
select ‘column1’,‘column2’,‘column3’
from table
where indexColumn between begin and end
limit num;
select 'column'
from table
where indexColumn is null;
%任意字符,出现任意次数
select ‘column1’,‘column2’,‘column3’
from table
where indexColumn like 'a%'
order by indexCloumn
_任意字符,出现一次
select ‘column1’,‘column2’,‘column3’
from table
where indexColumn like '_a_'
order by indexCloumn
select ‘column1’,‘column2’,‘column3’
from table
where indexColumn regexp ‘a’
order by indexCloumn
or
select ‘column1’,‘column2’,‘column3’
from table
where indexColumn regexp ‘app|d'
order by indexCloumn
escape special character
select ‘column1’,‘column2’,‘column3’
from table
where indexColumn regexp '\\.'
order by indexCloumn
select column1,upper(column1) as new_column
from table
order by column1
-- Left(str, len) | return left-handside of string
-- Right(str, len) | return right-handside of string
-- Length(str) | return length of string
-- Locate(pat, str, [pos]) | find sub-string location of string
-- SubString(str, pos, len) | get sub-string by index
-- Lower(str) | lower case of string
-- Upper(str) | upper case of string
-- Trim(str) | trim of string
-- LTrim(str) | trim left-handside of string
-- RTrim(str) | trim right-handside of string
-- Soundex(str) | return SOUNDEX value of string
-- Ceil() | return the smallest integer value not less than the argument
-- Floor() | return the largest integer value not greater than the argument
-- Ceiling() | return the smallest integer value not less than the argument
-- Truncate() | truncate to specified number of decimal places
-- Radians() | return argument converted to radians
-- Conv() | convert numbers between different number bases
-- Round() | round the argument
-- Asin() | return the arc sine
-- Acos() | return the arc cosine
-- Atan() | return the arc tangent
-- Sin() | return the sine of the argument
-- Cos() | return the cosine
-- Tan() | return the tangent of the argument
-- Cot() | return the cotangent
-- Pi() | return the value of pi
-- Ln() | return the natural logarithm of the argument
-- Log() | return the natural logarithm of the first argument
-- Log10() | return the base-10 logarithm of the argument
-- Log2() | return the base-2 logarithm of the argument
-- Exp() | raise to the power of
-- Pow() | return the argument raised to the specified power
-- Abs() | return the absolute value
-- Sqrt() | return the square root of the argument
-- Mod() | return the remainder
-- Sign() | return the sign of the argument
-- Rand() | return a random floating-point value
SELECT cust_id, order_num
FROM orders
WHERE Date(order_date) = '2005-09-01';
SELECT cust_id, order_num
FROM orders
WHERE Year(order_date) = 2005 AND Month(order_date) = 9;
-- more date functions
-- AddDate(expr, days) | add time values (intervals) to a date value
-- AddTime(expr1, expr2) | add time
-- Now() | return datetime
-- CurDate() | return current date
-- CurTime() | return current time
-- Date(expr) | return date part of expr
-- Time(expr) | return time part of expr
-- Year(expr) | return year part of expr
-- Month(expr) | return month part of expr
-- Day(expr) | return day part of expr
-- Hour(expr) | return hour part of expr
-- Minute(expr) | return minute part of expr
-- Second(expr) | return second part of expr
-- DayOfWeek(expr) | return day of expr
-- DateDiff(expr1, expr2) | return expr1 - expr2
Avg() Count() Max() Min() Sum()
select avg(column) as avg_column
from table
select count(column) as count_column
from table
select Sum(column) as sum_column
from table
GroupBy
SELECT vend_id, Count(*) AS num_prod
FROM products
GROUP BY vend_id;
Having
SELECT cust_id, Count(*) AS orders
FROM orders
GROUP BY cust_id
HAVING Count(*) >= 2;
select cust_id
from orders
where order_num in(
select order_num
from oorder_items
where prod_id = 'TNT2'
);
内连接和左外连接
SELECT customers.cust_id, orders.order_num
FROM customers INNER JOIN orders
ON customers.cust_id = orders.cust_id;
SELECT customers.cust_id, orders.order_num
FROM customers LEFT OUTER JOIN orders
ON customers.cust_id = orders.cust_id;
or
select vend_id,prod_id,prod_price
from products
where prod_price <= 5
or vend_id in (1001,1002)
insert into customers values('','','','','')
insert into customers ('','','','') values ('','','','')
-- UPDATE
UPDATE customers
SET cust_email = '[email protected]'
WHERE cust_id = 10005;
UPDATE customers
SET cust_email = '[email protected]', cust_name = 'jack'
WHERE cust_id = 10005;
-- DELETE
DELETE FROM customers
WHERE cust_id = 10006;
CREATE VIEW prodcust AS
SELECT cust_name, cust_contact, prod_id
FROM customers, orders, orderitems
WHERE customers.cust_id = orders.cust_id
AND orders.order_num = orderitems.order_num;
SELECT cust_name, cust_contact
FROM prodcust
WHERE prod_id = 'TNT2';
存在内存的数据库,缓存方向和分布式锁方向,使用redis比java自带的map或者guava笨蛋好吃具有一致性
定义:键值对数据库 键值对由字典保存 每个数据库都有一个相应的字典(键空间) 键是一个字符串对象,值可以是包括字符串,列表,哈希表,集合,有序集合在内的任意一种Redis类型对象
主从模式:一个实例作为主机,其余实例作为从机 ,数据完全一致 主机执行写数据命令,从机执行读数据命令, 实现读取分离
主从模式和哨兵模式全局存取变量,浪费内存 使用集群(分布式存储)
集群的优势在于高可用,写的操作多且数据量巨大,且不需要高级功能则考虑集群
哨兵的优势在于高可用,支持高级功能,且能在读的操作较多的场景下工作
主从的优势在于支持高级功能,且能在读的操作较多的场景下工作,但无法保证高可用,不建议在数据要求严格的场景下使用
延迟加载
读:先从缓存读,读不到就从数据库读,读完之后同步到缓存并添加缓存过期时间
写:只写数据库
直写
读:先从缓存读,读不到就从数据库读,读完之后同步到缓存且设置永不过期
写:先写数据库如何同步到缓存,设置为永不过期
如何选择
缓存问题
TCP/IP:四层网络协议 网络接口层 网络层 传输层 应用层
将域名和IP地址相互映射
TCP/IP 应用层协议
特点
HTTP/1.0中默认使用短连接,HTTP/1.1起,默认使用长连接 响应头加入Connection:keep-alive
Session 的主要作用就是通过服务端记录用户的状态。
Cookie 一般用来客户端保存用户信息
客户端请求消息格式:
服务端响应消息格式:
HTTP1.0:GET, POST 和 HEAD方法
HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。
SSL和HTTP 加密传输,身份认证
加密过程:
对称加密:密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有DES、AES等;
非对称加密:密钥成对出现(且根据公钥无法推知私钥,根据私钥也无法推知公钥),加密解密使用不同密钥(公钥加密需要私钥解密,私钥加密需要公钥解密),相对对称加密速度较慢,典型的非对称加密算法有RSA、DSA等。
SSL:安全套接层(Secure Socket Layer)
TLS:传输层安全性协议(Transport Layer Security)
TLS位于传输层和应用层之间,内部有TLS握手协议,TLS记录协议
HTTPS= HTTP + TLS
client:明文 + 公钥 = 密文
server:密文 + 私钥 = 明文
伪随机数生成器:秘钥生成随机性,更难被猜测
对称密码:对称密码使用的秘钥就是由伪随机数生成,相较于非对称密码,效率更高
消息认证码:保证消息信息的完整性、以及验证消息信息的来源
公钥密码:证书技术使用的就是公钥密码
数字签名:验证证书的签名,确定由真实的某个 CA 颁发
证书:解决公钥的真实归属问题,降低中间人攻击概率
传输层协议 面向连接,可靠的,基于字节流 有FTP、SMTP、HTTP
1、建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
2、服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
3、客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
两次握手可能出现网络延迟导致创建无效连接
1、当主机A的应用程序通知TCP数据已经发送完毕时,TCP向主机B发送一个带有FIN附加标记的报文段(FIN表示英文finish)。
2、主机B收到这个FIN报文段之后,并不立即用FIN报文段回复主机A,而是先向主机A发送一个确认序号ACK,同时通知自己相应的应用程序:对方要求关闭连接(先发送ACK的目的是为了防止在这段时间内,对方重传FIN报文段)。
3、主机B的应用程序告诉TCP:我要彻底的关闭连接,TCP向主机A送一个FIN报文段。
4、主机A收到这个FIN报文段后,向主机B发送一个ACK表示连接彻底释放。
为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
传输层流控 接收方告知发送方自己的窗口大小,控制发送方的发送速度
通过API通知TCP协议栈缩小TCP接收窗口
TCP的拥塞控制采用了四种算法,即 慢开始 、 拥塞避免 、快重传 和 快恢复
非面向连接的,不可靠的,传输快
路由转发协议
IP地址转换MAC地址
文件类型
cd usr: 切换到该目录下usr目录
cd ..(或cd../): 切换到上一层目录
cd /: 切换到系统根目录
cd ~: 切换到用户主目录
cd -: 切换到上一个操作所在目录
mkdir 目录名称: 增加目录
ls或者ll(ll是ls -l的别名,ll命令可以看到该目录下的所有目录和文件的详细信息):查看目录信息
find 目录 参数: 寻找目录(查)
mv 目录名称 新目录名称: 修改目录的名称(改)
mv 目录名称 目录的新位置
cp -r 目录名称 目录拷贝的目标位置: 拷贝目录(改),-r代表递归拷贝
rm [-rf] 目录: 删除目录(删)
touch 文件名称: 文件的创建(增)
cat/more/less/tail 文件名称 文件的查看(查)
cat: 查看显示文件内容
- more: 可以显示百分比,回车可以向下一行, 空格可以向下一页,q可以退出查看
- less: 可以使用键盘上的PgUp和PgDn向上 和向下翻页,q结束查看
- tail-10 : 查看文件的后10行,Ctrl+C结束 用tail -f main.log监控文件变化
vim 文件: 修改文件的内容(改)
rm -rf 文件: 删除文件(删)
tar -zcvf 打包压缩后的文件名 要打包压缩的文件
(z:调用gzip压缩命令进行压缩 c:打包文件 v:显示运行过程 f:指定文件名)
tar [-xvf] 压缩文件 -C /user 解压压缩包 -C 代表指定压缩的位置
d:代表目录
-:代表文件
l:代表软连接
r:代表权限是可读,r也可以用数字4表示
w:代表权限是可写,w也可以用数字2表示
x:代表权限是可执行,x也可以用数字1表示
所有者:ls ‐ahl命令 查看 chown 用户名 文件名 修改文件的所有者
所在组:s ‐ahl命令 看到文件的所有组 chgrp 组名 文件名 修改文件所在的组
修改文件/目录的权限的命令:chmod
chmod u=rwx,g=rw,o=r aaa.txt 等价于 chmod 764 aa.txt
useradd 选项 用户名:添加用户账号
userdel 选项 用户名:删除用户帐号
usermod 选项 用户名:修改帐号
passwd 用户名:更改或创建用户的密码
passwd -S 用户名 :显示用户账号密码信息
passwd -d 用户名: 清除用户密码
用户组管理
groupadd 选项 用户组 :增加一个新的用户组
groupdel 用户组:要删除一个已有的用户组
groupmod 选项 用户组 : 修改用户组的属性
String[] strs = new String[] {"aaa", "bbb", "ccc"};
List<String> list = Arrays.asList(strs);
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
String[] array = list.toArray(new String[list.size()]);
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
System.out.println(str);
if(///){
iterator.remove();//删除
}
}
并行指同一个时刻,并发指同一个时间段
进程和线程?
守护线程?
创建线程的方式?
class customThread extend Thread{
@override
public void run(){
}
}
class customRunnable implements Runnable{
public void run(){}
}
class customCallable implements Callable<String>{
public String call() throws Exception{
}
}
wait和sleep的区别?
Runnable 和 Callable 有什么区别?
notify()和notifyall()
等待池:线程调用对象的wait方法,线程释放对象的锁之后进入该对象的等待池,不去竞争该对象的锁
锁池:只有获取对象的锁,线程才能执行对象synchronized代码,对象的锁每次只能有一个线程获得,其余线程只能在锁池中等待
notify()随机唤醒该对象等待池中的一个线程,进入锁池;notifyAll()唤醒对象等待池中的所有线程进入锁池
创建线程池?
//定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量
static ExecutorService fixedExecutor = Executors.newFixedThreadPool(3);
fixedExecutor.execute(new Runnable() {
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " index:" + index);
}
});
//可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制
static ExecutorService cachedExecutor = Executors.newCachedThreadPool();
//定长线程池,可执行周期性的任务
static ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(3);
线程池状态:RUNNING SHUTDOWN STOP TIDYING TERMINATED
线程池中 submit() 和 execute()方法有什么区别?
多线程安全性问题
锁的级别:无锁->偏向锁->轻量级锁->重量级锁
死锁?多个线程相互持有所需要的资源,无法执行。
ThreadLocal
synchronized原理
synchronized和volatile区别?
synchronized和lock区别?
ReadWriteLock 如何使用?
使用读写锁实例
原子类的原理 CAS
CAS 包含 3 个参数,CAS(V, E, N)。V 表示需要更新的变量,E 表示变量当前期望值,N 表示更新为的值。只有当变量 V 的值等于 E 时,变量 V 的值才会被更新为 N。如果变量 V 的值不等于 E ,说明变量 V 的值已经被更新过,当前线程什么也不做,返回更新失败。
for(int i=0;i<n;i++){
new Thread(){
@override
public void run(){
for(int j=0; j< 10;j++){
try{
Thread.sleep(100);
}catch(InterruptedException){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}.start();
}
ForkJoinPool 解决CPU负载不均衡 较大的任务,被一个线程去执行,而其他线程处于空闲状态
package constxiong.interview.inject;
public class Bowl {
public void putRice() {
System.out.println("盛饭...");
}
}
package constxiong.interview.inject;
public class Person {
private Bowl bowl;
public void eat() {
bowl.putRice();
System.out.println("开始吃饭...");
}
public void setBowl(Bowl bowl) {
this.bowl = bowl;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="bowl" class="constxiong.interview.inject.Bowl" />
<bean id="person" class="constxiong.interview.inject.Person">
<property name="bowl" ref="bowl"></property>
</bean>
</beans>
package constxiong.interview.inject;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class InjectTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring_inject.xml");
Person person = (Person)context.getBean("person");
person.eat();
}
}
package constxiong.interview.inject;
public class Person {
private Bowl bowl;
public Person(Bowl bowl) {
this.bowl = bowl;
}
public void eat() {
bowl.putRice();
System.out.println("开始吃饭...");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="bowl" class="constxiong.interview.inject.Bowl" />
<bean id="person" class="constxiong.interview.inject.Person">
<constructor-arg name="bowl" ref="bowl"></constructor-arg>
</bean>
</beans>
public class quickSort {
public static void quickSort(int []array,int lo,int hi){
if(lo >= hi){
return;
}
int index = partition(array,lo,hi);
quickSort(array,lo,index-1);
quickSort(array,index+1,hi);
}
public static int partition(int []array,int lo,int hi){
int tmp = array[lo];;
while(lo < hi){
while(array[hi] >=tmp && lo<hi){
hi--;
}
array[lo] = array[hi];
while(array[lo] <=tmp && lo<hi){
lo++;
}
array[hi] = array[lo];
}
array[lo] = tmp;
return lo;
}
public static void main(String args[]){
int array[] = {2,9,-1,10,4};
quickSort(array,0,array.length-1);
System.out.print(Arrays.toString(array));
}
}
public class bubbbleSort {
public static void bubbleSort(int []array){
int n = array.length;
int tmp = 0;
for(int i=0;i<n-1;i++){
for(int j = 0;j< n-i-1;j++){
if(array[j+1]<array[j]){
tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
}
public static void main(String args[]){
int array[] = {2,9,-1,10,4};
bubbleSort(array);
System.out.print(Arrays.toString(array));
}
}
public class selectSort {
public static void selectSort(int array[]){
int n = array.length;
int tmp = 0;
for(int i = 0 ;i< n;i++){
int index = i;
for(int j =i;j<n;j++){
if(array[j]<array[index]){
index = j;
}
}
tmp = array[index];
array[index] = array[i];
array[i] = tmp;
}
}
public static void main(String args[]){
int array[] = {2,9,-1,10,4};
selectSort(array);
System.out.print(Arrays.toString(array));
}
}
public class binarySearch {
public static int binarySearch(int []array,int target){
if(array.length == 0){
return 0;
}
int start = 0;
int end = array.length-1;
while(start<=end){
int mid = start + (end-start)/2;
if(array[mid]==target){
return mid;
}else if(array[mid]>target){
end = mid-1;
}else {
start = mid+1;
}
}
return 0;
}
public static void main(String []args){
int array[] = {1,2,3,4,9,10,11};
System.out.print(binarySearch(array,9));
}
}
arr[] = {3,1,4,1,5,9,2,6,5}的最长递增子序列长度为4。即为:1,4,5,9
public static int MaxChildArrayOrder(int array[]){
int n = array.length;
int dp[] = new int[array.length];
for(int i=0;i<n;i++){
dp[i] = 1;
}
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
if(array[j]<array[i] && dp[j]+1>dp[j]){
dp[j] = dp[j]+1;
}
}
}
Arrays.sort(dp);
return dp[n-1];
}
arr[] = {6,-1,3,-4,-6,9,2,-2,5}的最大连续子序列和为14。即为:9,2,-2,5
public static int MaxContinueArraySum(int []array){
int sum = array[0];
int max = array[0];
for(int i=1;i<array.length;i++){
max = Math.max(max,max+array[i]);
if(max>sum){
sum = max;
}
}
return sum;
}
物品的个数为5,重量分别是weight[2,2,6,5,4],价值分别是value[6,3,5,4,6],给定一个容量为10的背包,如何装能装入最多财富
//输入数组前加一个0
public class Bag{
public static int getMaxValue(int[]weight,int value[],int m,int n){
int dp[][] = new int [m+1][n+1];//m个物品,n为背包容量
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(weight[i]>j){
dp[i][j] = dp[i-1][j];
}else{
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])
}
}
}
return dp[m][n];
}
}
//一维数组形式
public static int getMaxValue(int[]weight,int value[],int m,int n){
int dp[]= new int [n+1];
for(int i=0;i<m;i++){
for(int j=n;j>weight[i];j--){
if(dp[j-weight[i]]+value[i]>dp[j]){
dp[j] = (dp[j-weight[i]]+value[i];
}
}
}
return dp[n];
}
和01背包不同的是:每种物品可以重复取
public static int completeBag(int[] weight, int[] value, int m, int n){
int dp[] = new int[n+1];
for(int i=0;i<m;i++){
for(int j=weight[i];j<=n;j++){
if(dp[j-weight[i]]+value[i]>dp[j]){
dp[j]=dp[j-weight[i]]+value[i];
}
}
}
return dp[n];
}
有n件物品和容量为m的背包 给出i件物品的重量以及价值 还有数量 求解让装入背包的物品重量不超过背包容量 且价值最大
特点 它与完全背包有类似点 特点是每个物品都有了一定的数量
public static int mutiBag(int[] weight, int[] value,int[]number, int m, int n){
int dp[] = new int[n+1];
for(int i=0;i<m;i++){
for(int j=n;j>=0;j--){
for(int k =0; k<=number[i];k++){
if(j-k*weight[i]<0)break;
dp[j]=Math.max(dp[j],dp[j-k*weight[i]]+k*value[i]);
}
}
}
return dp[n];
}
给出一个非负整数数组,每一个整数表示一个竖立在坐标轴x位置的一堵高度为该整数的墙,选择两堵墙,和x轴构成的容器可以容纳最多的水
public int maxArea(int[] height) {
int i = 0, j = height.length - 1;
int max = 0;
while(i < j){
max = Math.max(max, Math.min(height[i], height[j])*(j-i));
if(height[i] < height[j]){
i++;
}else{
j--;
}
}
return max;
}
给定一个整形数组和一个数字s,找到数组中最短的一个连续子数组,使得连续子数组的数字和sum>=s,返回这个最短的连续子数组的长度值
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int i = 0, j = -1, sum = 0, minLength = Integer.MAX_VALUE;
while(i < nums.length){
if(j + 1 < nums.length && sum < s){
sum+=nums[++j];
}else{
sum-=nums[i++];
}
if(sum >= s){
minLength = Math.min(j-i+1, minLength);
}
}
if(minLength == Integer.MAX_VALUE){
return 0;
}
return minLength;
}
}
在一个连续的比特流中查找特定比特块出现的次数及首次出现的次数,比如在010011100001101110010中查找011出现的次数及首次出现的位置。
输入:int[] array, int target,整数的取值范围在[0,7]
输出:出现的次数times和出现的位置,没有找到次数返回0,位置返回-1
import java.util.ArrayList;
public class substringSearch {
static class bitRuslt{
private int times;
private int firstFound;
public int getTimes() {
return times;
}
public void setTimes(int times) {
this.times = times;
}
public int getFirstFound() {
return firstFound;
}
public void setFirstFound(int firstFound) {
this.firstFound = firstFound;
}
}
public static String intToString(int target){
if(target == 0){
return "000";
}else if(target == 1){
return "001";
}else if(target == 2){
return "010";
}else if(target == 3){
return "011";
}else if(target == 4){
return "100";
}else if(target == 5){
return "101";
}else if(target == 6){
return "110";
}else{
return "111";
}
}
public static bitRuslt find(int []array, int target){
bitRuslt result = new bitRuslt();
if(array.length == 0 || target > 7 || target < 0){
result.setTimes(0);
result.setFirstFound(-1);
return result;
}
StringBuffer arrString = new StringBuffer();
String targetStr = intToString(target);
for(int i=0;i<array.length;i++){
arrString.append(intToString(array[i]));
}
ArrayList<Integer> locations = new ArrayList<>();
int times = 0;
int firstFound = -1;
for(int i=0;i<arrString.length()-1;i++){
for(int j=0;j<3;j++){
if(targetStr.charAt(j) == arrString.charAt(i+j)){
if(j == 2){
times++;
locations.add(i);
}
continue;
}else {
break;
}
}
}
if(locations.size() == 0){
result.setFirstFound(-1);
}else {
result.setFirstFound(locations.get(0));
}
result.setTimes(times);
return result;
}
public static void main(String[]args){
int []arr = {2,3,4,1,5,6,2};
bitRuslt res = find(arr,3);
System.out.print(res.getTimes()+" "+res.getFirstFound());
}
}
显示中的时间是24进制的,时间表示也可以使用各种进制表示,例如2进制等等;先输入一个不知道是何种进制的时间如00002:00130,试求该时间表示的所有可能进制
输入:字符串, 例如00002:00130 每个字符的可能取值为[0,9] 或者[A ~ Z](分别代表10-35)
输出:所有可能存在的进制数,没有匹配的进制则返回0,有无穷多则返回-1;
输入样例:00002:00130
输出样例:4 5 6
本人菜鸡只做了45%
import java.util.ArrayList;
import java.util.Scanner;
public class shunfen2 {
public static int getMax(String str){
int max =0;
int num;
for(int i=0;i<str.length();i++){
if(str.charAt(i) >= 'A' && str.charAt(i) <= 'Z'){
num = (int)(str.charAt(i) - 'A' +10) ;
if(num > max){
max = num;
}
}else {
num = Integer.parseInt(String.valueOf(str.charAt(i)));
if(num > max){
max = num;
}
}
}
return max;
}
public static int conform(String str,int b){
int sum =0;
for(int i=0;i<str.length();i++){
if(str.charAt(i) >= 'A' && str.charAt(i) <= 'Z'){
sum += (int)(str.charAt(i) - 'A' +10) * Math.pow(b,str.length()-i-1);
}else {
sum += Integer.parseInt(String.valueOf(str.charAt(i))) * Math.pow(b,str.length()-i-1);
}
}
return sum;
}
public static void Solution(String []arr){
ArrayList<Integer> res = new ArrayList<>();
int Max = Math.max(getMax(arr[0]),getMax(arr[1]));
for(int i =Max+1;;i++){
if(conform(arr[0],i)>24 || conform(arr[0],i)<0){
break;
}
if(conform(arr[1],i)>60 || conform(arr[1],i)<0){
break;
}
res.add(i);
}
if(res.size() == 0){
System.out.print(-1);
}else {
for(int j =0;j<res.size();j++){
System.out.print(res.get(j)+" ");
}
}
}
public static void main(String []args){
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
String arr[] = input.split(":");
Solution(arr);
}
}
对一个HashMap数据结构按value值排序
public class HashMapSort {
public static void main(String []args){
HashMap<String,Integer> hashMap = new HashMap<String,Integer>();
hashMap.put("1",1);
hashMap.put("2",5);
hashMap.put("3",3);
hashMap.put("4",10);
hashMap.put("5",7);
System.out.println(hashMap);
List<HashMap.Entry<String,Integer>> list = new ArrayList<>(hashMap.entrySet());
Collections.sort(list, new Comparator<HashMap.Entry<String,Integer>>() {
@Override
public int compare(HashMap.Entry<String,Integer> o1, HashMap.Entry<String,Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
System.out.println(list);
}
}
读入和写入一个text文本文件
import java.io.*;
public class readAndWriteFile {
public static void read(){
String filePath = "input.txt";
try (FileReader reader = new FileReader(filePath);BufferedReader br = new BufferedReader(reader)){
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void write(){
try {
File writeName = new File("output.txt");
writeName.createNewFile();
try(FileWriter writer = new FileWriter(writeName);BufferedWriter out = new BufferedWriter(writer)){
out.write("zhangchangwei\r\n");
out.write("jiayou\r\n");
out.flush();//缓存区内容压入文件
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String args[]){
read();
write();
}
}
持续更新中