常见QA

目录

1. 机器学习模型

1) 机器学习和深度学习的区别是什么?

2) 有监督机器学习和无监督机器学习的区别?

3) k -means和KNN是什么?

4) 当前的「三大」最常见的机器学习任务:

5) 两大数据降维(Dimensionality Reduction)问题:

7)正则化

8) 逻辑回归:

2. 深度学习

1)激活函数:https://www.jianshu.com/p/304a87fcef43

2)梯度消失,梯度爆炸:

3. 常见算法及程序

1)快排

2) 文件创建、读取

3)单例模式

4. 链表

5. 常见问题

1)http转发和重定向

2)重写和重载

3)Final finally finalize

4)String stringbuffer

5)List set map

6)数据库事务死锁

7)Public private protected

8)事务

9)JAVA反射机制

10) Get和post

11) 数据库大文件和图片存储

12) Drop delete truncate


 

1. 机器学习模型

1) 机器学习和深度学习的区别是什么?

机器学习是人工智能的一个子集,它为机器提供了无需任何显式编程就能自动学习和改进的能力。而深度学习是机器学习的一个子集,人工神经网络能够做出直观的决定。

2) 有监督机器学习和无监督机器学习的区别?

在监督学习中,机器在有标记数据的帮助下进行训练,即,即带有正确答案标记的数据。而在无监督机器学习中,模型是通过自身发现信息来学习的。与有监督学习模型相比,无监督学习模型更适合于执行困难的处理任务。

示例:有监督学习:kNN, SVM,BP,RF,GBDT,LR,;无监督学习:k-means

3) k -means和KNN是什么?

K-means是一种用于聚类问题处理的无监督算法,KNN或K近邻是一种用于回归和分类处理的有监督算法。

4) 当前的「三大」最常见的机器学习任务:

回归(Regression):回归是一种用于连续型数值变量预测和建模的监督学习算法,特征是具有数值型目标变量的标注数据集,即一个用以监督算法的观察样本,都有一个数值型真值。

分类(Classification):分类是一种用于分类变量建模及预测的监督学习算法,正如你所见,许多回归算法都有其对应的分类形式,分类算法往往适用于类别(或其可能性)的预测,而非数值。

聚类(Clustering):聚类是基于数据内部结构来寻找样本自然族群(集群)的无监督学习任务,由于聚类属于无监督学习,也就不会输出“正确的答案”,评价结果时往往要用到数据可视化。如果你需要“正确的答案”,亦即训练集中存在预标注的集群,那么用分类算法会更加合适。

分类与回归区别:这两个概念都是监督机器学习技术的一个重要方面。通过分类,将输出划分为不同的类别进行预测。而回归模型通常用来找出预测与变量之间的关系。分类和回归的一个关键区别是前者的输出变量是离散的,而后者是连续的。

5) 两大数据降维(Dimensionality Reduction)问题:

在机器学习领域,“维度(Dimensionality)”通常指数据集中的特征数量(即输入变量的个数)。

当特征的个数特别大的时候(相对于数据集中观测样本的数量来说),训练出一个有效的模型,对算法要求就会特别高(即,用现有的算法训练出一个有效的模型特别困难)。这就是所谓的“维度灾难(Curse of Dimensionality)”,特别是对依赖于距离计算的聚类算法而言。

特征选取(Feature Selection):特征选取是从你的数据集中过滤掉不相关或冗余的特征

特征提取(Feature Extraction):在原特征集的基础上重新构造出一些(一个或多个)全新的特征。

特征选取与特征提取的关键区别在于:特征选取是从原特征集中选取一个子特征集,而特称提取则是在原特征集的基础上重新构造出一些(一个或多个)全新的特征。

6)过拟合和欠拟合:

避免过拟合方法:保持模型简单,第二种方法是使用交叉验证技术,第三种方法是使用正则化技术,例如LASSO。

7)正则化

正则化是针对过拟合而提出的,以为在求解模型最优的是一般优化最小的经验风险,现在在该经验风险上加入模型复杂度这一项(正则化项是模型参数向量的范数),并使用一个rate比率来权衡模型复杂度与以往经验风险的权重,如果模型复杂度越高,结构化的经验风险会越大,现在的目标就变为了结构经验风险的最优化,可以防止模型训练过度复杂,有效的降低过拟合的风险。奥卡姆剃刀原理,能够很好的解释已知数据并且十分简单才是最好的模型。

使用L1L2正则化,为什么可以降低模型的复杂度模型越复杂,越容易过拟合,这大家都知道,加上L1正则化给了模型的拉普拉斯先验,加上L2正则化给了模型的高斯先验。从参数的角度来看,L1得到稀疏解,去掉一部分特征降低模型复杂度。L2得到较小的参数,如果参数很大,样本稍微变动一点,值就有很大偏差,这当然不是我们想看到的,相当于降低每个特征的权重。

8) 逻辑回归

得不到非线性关系,实际问题不能完全用线性关系拟合。

 

 

  • 2. 深度学习

1)激活函数:https://www.jianshu.com/p/304a87fcef43

Sigmod:=1/(1+e^-x)

优点:输出值0-1(很重大的优点);其余的和其他众多激活函数比起来,感觉没有什么优点,方便入门理解

缺点:容易梯度消失;x的可变值区域太小,极其容易陷入级值的状况(-0.9~0.9);指数exp计算复杂。

Tanh: =(e^x – e^-x)/(e^x + e^-x)

优点:和sigmod比起来,是零均值化处理。(零均值化可以加快模型的收敛);

缺点:和sigmod一样的缺点

Relu: =max(0,x)

优点:计算复杂度低(只有一个if>0判断,大于0则激活值为1),部分区域线性递增,没有幂运算与指数运算;

缺点:x小于0时无法产生激活值;训练到后期可能权重参数更新太大

如何选择激活函数?不要sigmod

2)梯度消失,梯度爆炸:

当网络层数过多时,前面层由于求导过程乘积运算,出现weight与bias变得异常大与异常小的情况

 

 

3. 常见算法及程序

1)快排

package com.nrsc.sort;

public class QuickSort {
	public static void main(String[] args) {
		int[] arr = { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };
		quickSort(arr, 0, arr.length - 1);
		System.out.println("排序后:");
		for (int i : arr) {
			System.out.println(i);
		}
	}

	private static void quickSort(int[] arr, int low, int high) {

		if (low < high) {
			// 找寻基准数据的正确索引
			int index = getIndex(arr, low, high);

			// 进行迭代对index之前和之后的数组进行相同的操作使整个数组变成有序
			quickSort(arr, 0, index - 1);
			quickSort(arr, index + 1, high);
		}

	}

	private static int getIndex(int[] arr, int low, int high) {
		// 基准数据
		int tmp = arr[low];
		while (low < high) {
			// 当队尾的元素大于等于基准数据时,向前挪动high指针
			while (low < high && arr[high] >= tmp) {
				high--;
			}
			// 如果队尾元素小于tmp了,需要将其赋值给low
			arr[low] = arr[high];
			// 当队首元素小于等于tmp时,向前挪动low指针
			while (low < high && arr[low] <= tmp) {
				low++;
			}
			// 当队首元素大于tmp时,需要将其赋值给high
			arr[high] = arr[low];

		}
		// 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置
		// 由原理部分可以很清楚的知道low位置的值并不是tmp,所以需要将tmp赋值给arr[low]
		arr[low] = tmp;
		return low; // 返回tmp的正确位置
	}
}

2) 文件创建、读取

文件的创建,写入,读取
public void fileOperate(String path,String filename) throws IOException{

//文件创建
File filepath=new File(path);
if(!filepath.exists()){
filepath.mkdir();
}
File file=new File(path+"/"+filename);
    if(!file.exists()){
    	file.createNewFile();
}

//文件写入
FileOutputStream out=new FileOutputStream(file,true);        
for(int i=0;i<10000;i++){
StringBuffer sb=new StringBuffer();
    sb.append(i.toString());
    out.write(sb.toString().getBytes("utf-8"));
}
out.close();

//文件读取:
FileInputStream fis=new FileInputStream(file);
byte[] buf = new byte[1024];
StringBuffer sb=new StringBuffer();
while((fis.read(buf))!=-1)...{
sb.append(new String(buf));    
    buf=new byte[1024];//重新生成,避免和上次读取的数据重复
}
system.printIn.out(sb.toString());
}

3)单例模式


public class SingletonDemo {
    private static SingletonDemo instance; //对象
    private SingletonDemo(){} //构造函数
    public static synchronized SingletonDemo getInstance(){ // getInstance()方法
        if (instance == null) {
            instance = new SingletonDemo1();
        }
        return instance;
    }
}

静态内部类:
public class SingletonDemo5 {
    private static class SingletonHolder{
        private static final SingletonDemo5 instance = new SingletonDemo5();//对象放在内部类里,Singleton类被装载了,instance不一定被初始化,延迟加载
    }
    private SingletonDemo5(){}
    public static final SingletonDemo5 getInsatance(){
        return SingletonHolder.instance;
    }
}

4)冒泡排序

public void bubbleSort(Integer[] arr, int n) {
        if (n <= 1) return;       //如果只有一个元素就不用排序了
 
        for (int i = 0; i < n; ++i) {
            // 提前退出冒泡循环的标志位,即一次比较中没有交换任何元素,这个数组就已经是有序的了
            boolean flag = false;
            for (int j = 0; j < n - i - 1; ++j) {        //此处你可能会疑问的j<n-i-1,因为冒泡是把每轮循环中较大的数飘到后面,
                // 数组下标又是从0开始的,i下标后面已经排序的个数就得多减1,总结就是i增多少,j的循环位置减多少
                if (arr[j] > arr[j + 1]) {        //即这两个相邻的数是逆序的,交换
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    flag = true;
                }
            }
            if (!flag) break;//没有数据交换,数组已经有序,退出排序
        }
    }

 

4. 链表

链表是一种动态的数据结构,因为在创建链表时,我们不需要知道链表的长度,当插入一个结点时,只需要为该结点分配内存,然后调整指针的指向来确保新结点被连接到链表中。所以,它不像数组,内存是一次性分配完毕的,而是每添加一个结点分配一次内存。正是因为这点,所以它没有闲置的内存,比起数组,空间效率更高。

基本操作

创建节点

struct Node
{
	int a;				//数据域
	struct Node* next;	//指针域(指向节点的指针)
};

 定义链表头尾指针

struct Node* head= NULL;
struct Node* end = NULL;

创建链表:

void AddListTill(int a )
{
		//创建一个节点
		struct Node* temp=(struct Node*)malloc(sizeof(struct Node));		//此处注意强制类型转换

		//节点数据进行赋值
		temp->a=a;
		temp->next=NULL;		
		
		//连接分两种情况1.一个节点都没有2.已经有节点了,添加到尾巴上
		if(NULL==head)
		{	
			head=temp;
		}
		else
		{
		    end->next=temp;		//尾结点应该始终指向最后一个
		}
		end=temp;			//尾结点应该始终指向最后一个
}

遍历链表

void ScanList()
{
	struct Node *temp =head;		//定义一个临时变量来指向头
	while (temp !=NULL)
	{
		printf("%d\n",temp->a);
		temp = temp->next;		//temp指向下一个的地址 即实现++操作
	}

}

 

 

 

5. 常见问题

1)http转发和重定向

  1. 语句写法:转发forward,重定向sendRedirect;
  2. 请求次数:转发是一次请求,重定向是2次请求;
  3. C/S: 转发是服务器行为,重定向是客户端行为;
  4. 地址栏是否改变:转发后,地址栏显示的信息不改变,而重定向后,是开始了一个新的Http请求,因此地址栏是redirect后的地址。

详细:转发是服务器内部重定向,程序收到请求后重新定向到另一个程序,客户机并不知道;重定向则是服务器收到请求后发送一个状态头给客户,客户将再请求一次,这里多了两次网络通信的来往。

2)重写和重载

方法重写(overriding): 也叫子类的方法覆盖父类的方法,要求返回值、方法名和参数都相同。子类异常不能超出父类异常;子类访问级别不能低于父类访问级别。

方法重载(overloading): 重载是在同一个类中的两个或两个以上的方法,拥有相同的方法名,但是参数却不相同,方法体也不相同,最常见的重载的例子就是类的构造函数。

3)Final finally finalize

  1. final

final修饰类,说明这个类不能被继承,是以个顶级类。

final修饰变量,说明这个变量是常量。

final修饰方法,表示这个方法不能被重写,不过可以重载final方法。

    2. finally

  finally是关键字,在异常处理中,try子句中执行需要运行的内容,catch子句用于捕获异常,finally子句表示不管是否发生异常,都会执行。finally可有可无。但是try…catch必须成对出现。

   3. finalize()

  finalize()是Object类的方法,Java 技术允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象进行调用。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的子类覆盖 complete() 方法以整理系统资源或者执行其他清理操作。

 

4)String stringbuffer

StringBuffer长度和内容可变,String内容和长度不可变。如果使用StringBuffer做字符串的拼接,不会浪费太多的资源。

(1)String是内容不可变的,而StringBuffer、StringBuilder都是内容可变的。

(2)StringBuffer是同步的,数据安全的,但是效率低;StringBuilder是不同步的,数据不安全,效率高。一般情况使用stringbuilder。

 

5)List set map

Collection是最基本的集合接口,声明了适用于JAVA集合的通用方法。Set 和List 都继承了Conllection,而Map不是继承自Collection接口。Collection、List、Set、Map都是接口,不能实例化。继承自它们的 ArrayList, Vector, HashTable, HashMap是具象class,这些才可被实例化。

1)Set: 不排序,没有重复对象。 Set接口主要实现了两个实现类:

HashSet: HashSet类按照哈希算法来存取集合中的对象,存取速度比较快。

TreeSet:TreeSet类实现了SortedSet接口,能够对集合中的对象进行排序。

2)List:线性方式存储,元素可重复。List接口主要实现类包括:

ArrayList() : 代表长度可以改变的数组。可以对元素进行随机的访问,向ArrayList()中插入与删除元素的速度慢。

LinkedList(): 在实现中采用链表数据结构。插入和删除速度快,访问速度慢。

Vector:变长数组算法

Vector和HashTable是线程同步的(synchronized)。性能上ArrayList和HashMap分别比Vector和Hashtable要好。

3) Map: 是一种把键对象和值对象映射的集合,Map没有继承于Collection接口。

HashMap:线程不安全,高效,支持null;

HashTable:线程同步安全,低效,不支持null;

TreeMap

 

6)数据库事务死锁

数据库是一个多用户使用的共享资源,当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。

  1. 事务之间对资源访问顺序的交替

出现原因:

一个用户A 访问表A(锁住了表A),然后又访问表B;另一个用户B 访问表B(锁住了表B,然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。

解决方法: 这种死锁比较常见,是由于程序的BUG产生的,除了调整程序的逻辑没有其它的办法。仔细分析程序的逻辑,对于数据库的多表操作时,尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源,如操作A和B两张表时,总是按先A后B的顺序处理, 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源。

  1. 并发修改同一记录

出现原因:

 用户A查询一条纪录,然后修改该条纪录;这时用户B修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。这种死锁由于比较隐蔽,但在稍大点的项目中经常发生。

解决方法:

a. 使用乐观锁进行控制。乐观锁大多是基于数据版本(Version)记录机制实现。即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个“version”字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。乐观锁机制避免了长事务中的数据库加锁开销(用户A和用户B操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系统整体性能表现。Hibernate 在其数据访问引擎中内置了乐观锁实现。需要注意的是,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。

b. 使用悲观锁进行控制。悲观锁大多数情况下依靠数据库的锁机制实现,如Oracle的Select … for update语句,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户账户余额),如果采用悲观锁机制,也就意味着整个操作过程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对成百上千个并发,这样的情况将导致灾难性的后果。所以,采用悲观锁进行控制时一定要考虑清楚。

c. SqlServer可支持更新锁

为解决死锁,SqlServer引入更新锁,它有如下特征:

(1) 加锁的条件:当一个事务执行update语句时,数据库系统会先为事务分配一把更新锁。

(2) 解锁的条件:当读取数据完毕,执行更新操作时,会把更新锁升级为独占锁。

(3) 与其他锁的兼容性:更新锁与共享锁是兼容的,也就是说,一个资源可以同时放置更新锁和共享锁,但是最多放置一把更新锁。这样,当多个事务更新相同的数据时,只有一个事务能获得更新锁,然后再把更新锁升级为独占锁,其他事务必须等到前一个事务结束后,才能获取得更新锁,这就避免了死锁。

(4) 并发性能:允许多个事务同时读锁定的资源,但不允许其他事务修改它

3) 索引不当导致全表扫描

出现原因:

如果在事务中执行了一条不满足条件的语句,执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。类似的情况还有当表中的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,最终发生阻塞或死锁。

解决方法:

SQL语句中不要使用太复杂的关联多表的查询;使用“执行计划”对SQL语句进行分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。

4个必要条件:

1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。

2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

 

7)Public private protected

1、public:public表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用

2、private:private表示私有,私有的意思就是除了class自己之外,任何人都不可以直接使用,私有财产神圣不可侵犯嘛,即便是子女,朋友,都不可以使用。

3、protected:protected对于子女、朋友来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部class,protected就变成private。

4. friendly:当前类和同一的类可公用。

 

8)事务

  1. 事务:数据库事务(特点:一致性)和Spring事务(我讲到了hibernate)

数据库事务:数据库事务是指作为单个逻辑工作单元执行的一系列操作(SQL语句)。这些操作要么全部执行,要么全部不执行。通过ACID实现数据库事务模型(四个属性)

1) 原子性(Atomicity):事务是数据库的逻辑工作单位,它对数据库的修改要么全部执行,要么全部不执行。

2) 一致性(Consistemcy):事务执行前后,数据库的状态都满足所有的完整性约束

3) 隔离性(Isolation):并发执行的事务是隔离的,保证多个事务互不影响。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。通过设置数据库的隔离级别,可以达到不同的隔离效果。

4) 持久性(Durability):一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。

事务运行的三种模式

1)自动提交事务:默认事务管理模式。如果一个语句成功地完成,则提交该语句;如果遇到错误,则回滚该语句。

2)显式事务:以BEGIN TRANSACTION显式开始,以COMMIT或ROLLBACK显式结束。

3)隐性事务:当连接以此模式进行操作时,sql将在提交或回滚当前事务后自动启动新事务。无须描述事务的开始,只需提交或回滚每个事务。它生成连续的事务链。

隔离级别:级别越来越高,并发性越来越差,安全性越来越高

读未提交(read-uncommitted)-》脏读

读已提交(read-committed)-》不可重复读

可重复读-》幻读

顺序读

 

9)JAVA反射机制

简单说,反射机制值得是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息

在运行时能够判断任意一个对象所属的类;

在运行时构造任意一个类的对象

在运行时判断任意一个类所具有的成员变量和方法

在运行时调用任一对象的方法

在运行时创建新类对象

哪里用到反射机制:

Class.forName('com.MySQL.jdbc.Driver.class').newInstance();那个时候只知道生成驱动对象实例,后来才知道,这就是反射,现在很多框架都用到反射机制,hibernate,struts都是用反射机制实现的。采用反射机制的话,开发时,新增或更改功能时,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。

 

10) Get和post

http的不同方法:

1. 传参方式:GET使用URL或Cookie传参。而POST将数据放在BODY中。

2. 长度:GET的URL的长度会有长度上的限制, POST的数据则可以非常大。

3. 安全性:GET没有POST安全,因为数据在地址栏上可见。

4. 速度:get更快。

5. 用途:get用于搜索排序和筛选(淘宝搜索);post用于修改和写入数据。

  (1)post更安全(不会作为url的一部分,不会被缓存、保存在服务器日志、以及浏览器浏览记录中)

(2)post发送的数据更大(get有url长度限制)

(3)post能发送更多的数据类型(get只能发送ASCII字符)

(4)post比get慢

(5)post用于修改和写入数据,get一般用于搜索排序和筛选之类的操作(淘宝,支付宝的搜索查询都是get提交),目的是资源的获取,读取数据

 

11) 数据库大文件和图片存储

  1. 把图片直接以二进制形式存储在数据库中

一般数据库提供一个二进制字段来存储二进制数据。比如mysql中有个blob字段。oracle数据库中是blob或bfile类型

    2. 图片存储在磁盘上,数据库字段中保存的是图片的路径。

 

12) Drop delete truncate

drop table  tb删除内容和定义,释放空间。简单来说就是把整个表去掉.

truncate table tb删除内容、释放空间但不删除定义。只是清空表数据;

delete table tb where 条件 删除内容不删除定义,不释放空间

 

13)深拷贝 浅拷贝

深拷贝就是拷贝对象本身的副本,而浅拷贝只是拷贝了对象的引用,此时会出现一个对象有被两个指针所指向。

浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间;

深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝。

经浅拷贝后的指针是指向两个不同地址的指针。两个指针指向同一个内存空间,在两次析构函数后,会造成内存泄漏。所以,在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。

 

14)数据库基本概念

-备份:物理备份(归档模式/热备份,非归档模式/冷备份),逻辑备份(对数据库的导入导出操作)。

-数据恢复:完全恢复(恢复到失败时),不完全(恢复到失败前);物理恢复,逻辑恢复。

-锁:共享锁(读锁),排他锁(写锁)

  • 存储过程:sql+控制流语句
  • 触发器:特殊的存储过程,不需调用自动执行
  • 约束:主键约束,外键约束(参照约束/参照完整性),唯一约束,非空约束,检查约束
  • 范式:1)第一范式:列不可分割;2)第二范式:不能非主键对主键部分函数依赖;(否则数据冗余,更新/插入/删除异常,通常不使用复合主键)3)第三范式:不存在函数传递依赖。
  • Select category avg(productprice) 平均价格

From productinfo

Group by category

having avg(productprice)>20

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章