一 、 数据库三大范式和反范式
1. 详见: https://blog.csdn.net/chenyyhh92/article/details/51174343
-
1NF、列不可分;(例如地址字段,我们应该分成多个字段显示,保证原子性)
-
2NF、不存在部分依赖;(把没关系的几个字段放到同一个表当中,我们应该把它们分开来)
-
3NF、不存在传递依赖。(需要另外一个表的数据,只需要引入另一个表的主键即可)
2. 反三范式
- 概念:没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,提高读性能,就必须
降低范式标准
,适当保留冗余数据
。 - 做法: 在概念数据模型设计时遵守第三范式,
降低范式标准
的工作放到物理数据模型设计时考虑。降低范式就是增加字段,减少了查询时的关联,提高查询效率,因为在数据库的操作中查询的比例要远远大于DML的比例。但是反范式化一定要适度,并且在原本已满足三范式的基础上再做调整的。
二、快速失败和安全失败
https://blog.csdn.net/chenssy/article/details/38151189
1. 快速失败(fail-fast)
概念:在使用迭代器对集合对象进行遍历的时候,如果 A 线程正在对集合进行遍历,此时 B 线程对集合进行修改(增加、删除、修改),或者 A 线程在遍历过程中对集合进行修改,都会导致 A 线程抛出 ConcurrentModificationException 异常。
错误原因:底层ArrayList迭代代码中,它增删改都会先判断当前(迭代时)集合长度是否和迭代之前长度相等,否者抛出这个异常。
public static void a(){
//1. 创建集合并存入数据
List list = new ArrayList();
for(int i = 0 ; i < 10 ; i++){
list.add(i);
}
//2. 遍历出集合中所有值
for (Object i:list){
System.out.println("遍历:"+i);
//2.1 判断:如果集合中元素为3时,删除该元素
if(i.equals(3)){
list.remove(i);//造成快速失败的原因
}
}
}
执行后的效果如下图:
2. 安全失败(fail-safe)
使用CopyOnWriteArrayList来替换ArrayList。推荐使用该方案。
原理:
- 任何对array在结构上有所改变的操作(add、remove、clear等),CopyOnWriterArrayList都会
copy现有的数据
,再在copy的数据上修改
,这样就不会影响COWIterator中的数据了,修改完成之后改变原有数据的引用即可。 - 同时这样造成的代价就是
产生大量的对象
,同时数组的copy也是相当有损耗
的。
public static void b(){
//1. 创建集合并存入数据
List list = new CopyOnWriteArrayList();
for(int i = 0 ; i < 10 ; i++){
list.add(i);
}
//2. 遍历出集合中所有值
for (Object i:list){
System.out.println("遍历:"+i);
//2.1 判断:如果集合中元素为3时,删除该元素
if(i.equals(3)){
list.remove(i);
}
}
}
注意:
- Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。
- java.util包下面的所有的集合类都是快速失败的。快速失败的迭代器会抛出ConcurrentModificationException异常。当在迭代一个集合的时候,如果有另外一个线程在修改这个集合,就会抛出ConcurrentModification异常。不能在多线程下发生并发修改。
- 查看ArrayList源代码,在next方法执行的时候,会执行checkForComodification()方法
三、Class.forName(“XXX”) 、.class和.getClass()的作用
http://www.cnblogs.com/Seachal/p/5371733.html
https://blog.csdn.net/sinat_38259539/article/details/71799078 (详细介绍怎么使用反射来运行java类)
https://www.jianshu.com/p/13991ec28dd3)(获取注解)
1、Java反射
概念:
- 在运行时期获取对象类型信息的操作。
- 传统的编程方法要求程序员在编译阶段决定使用的类型,但是在反射的帮助下,编程人员可以动态获取这些信息,从而编写更加具有可移植性的代码。
- 严格地说,反射并非编程语言的特性,因为在任何一种语言都可以实现反射机制,但是如果编程语言本身支持反射,那么反射的实现就会方便很多。
2、Class.forName(“XXX”) 、getClass()和.class :获得Class对象
概念:
- jvm中有N多的实例每个类都有该Class对象。
- Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
注意:在运行期间,一个类,只有一个Class对象产生。
package sdasd;
/**
* 获取Class对象的三种方式
* 1 Object ——> getClass();
* 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
* 3 通过Class类的静态方法:forName(String className)(常用)
*
*/
public class Fanshe {
public static void main(String[] args) {
//第一种方式获取Class对象
Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象
System.out.println(stuClass.getName());
//第二种方式获取Class对象
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
//第三种方式获取Class对象(常用)
try {
Class stuClass3 = Class.forName("sdasd.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
获得Class对象的信息:
- getName():String:获得该类型的全称名称。
- getSuperClass():Class:获得该类型的直接父类,如果该类型没有直接父类,那么返回null。
- getInterfaces():Class[]:获得该类型实现的所有接口。
- isArray():boolean:判断该类型是否是数组
- isEnum():boolean:判断该类型是否是枚举类型。
- getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
- getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
- … …
3、Class.forName().newInstance():动态加载类(另一种new对象的方法)
public static void a() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
c c1 = new c();
//这里面参数是完整的类路径
//Class.forName(xxx.xx.xx) 返回的是一个类,
//.newInstance() 后才创建一个对象
//Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段
Object c2 = Class.forName("sdasd.c").newInstance();
System.out.println("c对象:" + c1);
System.out.println("c1对象:" + c2);
}
与new 对象的区别:
- Class.forName()在运行时加载;
Class.class和getClass()是在编译时加载. - new ClassName(),就是所谓的静态加载,
Class.forName(“ClassName”),就是所谓的动态加载。 “静态加载”
的类在编译的时候就要提供
,而动态加载
的类在源程序编译时可以缺席,在运行时按需提供
4、Class对象的生成的原理
public static void main(String[] args) throws ClassNotFoundException {
// 测试.class
// 1个都不调用
Class testTypeClass = TestClassType.class;
System.out.println("测试.class:" + testTypeClass);
System.out.println();
// 测试Class.forName()
// 只会调用 static代码块
Class testTypeForName = Class.forName("sdasd.TestClassType");
System.out.println("测试Class.forName():" + testTypeForName);
System.out.println();
// 测试Object.getClass()
// 3个代码块都会调用
TestClassType testTypeGetClass = new TestClassType();
System.out.println("测试Object.getClass():" + testTypeGetClass.getClass());
}
class TestClassType {
// 构造函数
public TestClassType() {
System.out.println("---构造函数---");
}
// 静态的参数初始化
static {
System.out.println("---静态的参数初始化---");
}
// 非静态的参数初始化
{
System.out.println("---非静态的参数初始化---");
}
}
运行结果;
总结:
- 类名.class: JVM将使用类装载器, 将类装入内存(前提是:类还没有装入内存),不做类的初始化工作.返回Class的对象
- Class.forName(“类名字符串”): (注:类名字符串是包名+类名) 装入类,并做类的静态初始化,返回Class的对象
- 实例对象.getClass(): 对类进行静态初始化、非静态初始化;返回引用运行时真正所指的对象(因为:子对象的引用可能会赋给父对象的引用变量中)所属的类的Class的对象
生成Class对象的过程:
- 当我们编写一个新的java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。
五、o(1), o(n), o(logn), o(nlogn)
https://blog.csdn.net/Mars93/article/details/75194138
作用:这是算法的时空复杂度的表示。
- o(1):
最低的时空复杂度
了,也就是耗时/耗空间与输入数据大小无关,无论输入数据增大多少倍,耗时/耗空间都不变; 哈希算法就是典型的O(1)时间复杂度,无论数据规模多大,都可以在一次计算后找到目标 - o(n):就代表
数据量增
大几倍,耗时也增大
几倍;比如常见的遍历算法。 - o(logn):当数据增大n倍时,耗时增大logn倍;二分查找就是O(logn)的算法,
每找一次排除一半的可能
,256个数据中查找只要找8次就可以找到目标。 - o(nlogn):n乘以logn,
当数据增大256倍时,耗时增大256*8=2048倍
;这个复杂度高于线性低于平方。归并排序就是O(nlogn)的时间复杂度
复杂度排序:o(1) < o(n) < o(logn) < o(nlogn)
六、深拷贝浅拷贝
https://blog.csdn.net/baiye_xing/article/details/71788741
浅拷贝: 浅拷贝仅仅复制所拷贝的对象,而不复制它所引用的对象
深拷贝: 深拷贝把要复制的对象所引用的对象都复制了一遍。(不再指向同一个对象的引用)
检验深拷贝成功:改变任意一个新对象/数组中的属性/元素, 都不改变原对象/数组
七、java 子类强转父类 父类强转子类
https://www.cnblogs.com/ooo0/p/9308583.html
1. Java 子类强转父类
- java中子类强转父类,实际上依然是子类;
- 该引用只能调用父类中定义的方法和变量;
- 如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;
2. Java 父类强转子类
- 只有父类对象本身就是用子类new出来的时候, 才可以在将来被强制转换为子类对象.