安全编程期末复习
安全编程期末复习
Java的8种基本数据类型
序号 | 数据类型 | 位数 | 默认值 | 取值范围 | 举例说明 |
---|---|---|---|---|---|
1 | byte(位) | 8 | 0 | byte b = 10; | |
2 | short(短整数) | 16 | 0 | short s = 10; | |
3 | int(整数) | 32 | 0 | int i = 10; | |
4 | long(长整数) | 64 | 0 | long l = 10l; | |
5 | float(单精度) | 32 | 0.0 | float f = 10.0f; | |
6 | double(双精度) | 64 | 0.0 | double d = 10.0d; | |
7 | char(字符) | 16 | null | char c = ‘c’; | |
8 | boolean(布尔值) | 8 | false | true、false | boolean b = true; |
享元设计模式flyweight
享元模式的意图是通过共享有效支持大量细粒度的对象,来提高应用程序的性能,节省系统中重复创建对象实例的性能消耗。
何时使用享元模式
-
系统中有大量对象时
-
这些对象消耗大量内存时
-
这些对象的状态大部分可以外部化时
例如:我们在使用输入的法的时候,如果我们每个字都是new一个对象实例的操作,那么内存中的实例就太可怕,这个时候我们可以在内存中创建26个字母的对象,然后重复利用他们。对于需要出现在不同位置的字符,我们可以通过调用其内部的方法来实现,比如字母i需要被放在(x, y)这个位置,就调用, i.display(x, y)
反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射就是把一个java类中的每一个成分解析成一个个java类。
一个java类用一个Class类的对象来表示,一个类的组成部分:成员变量,方法, 构造方法等信息用一个个的类来表示。这个类的Class需要提供一系列的方法来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息使用类的实例对象来表示,他们是Field,Method,Contructor,Package等。
一个class中的每个成员都可以用相应的API反射类的对象来表示。
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
反射为什么用的少?
因为效率低,不到万不得已不会用反射
反射的高明之处在于?
可在程序运行过程中修改代码(比如mysql改成mssql)
Java加载类的原理
当我们定义一个对象Person p= new Person(),java就从把person的字节码加载到内存 中,然后用这个字节码产生对象。
每用到一个新类,就会加载一个新的字节码,每个字节码就是Class的实例对象。
对于Class对象,不存在可供调用的构造函数。
得到一个类的Class对象的方法
-
Person.class
-
p1.getClass()
-
Class.forName(“java.lang.String”)
其中当jvm中没有这个加载这个类的时候,只能用3来获得这个类的Class。
方法3最常见。
为什么class对象在java虚拟机中只可能是0或1个
因为创建的是类加载器先在hashmap寻找有没有,没有才创建类加载器原理
类装载器的委托模式
每个ClassLoader本身只能分别加载特定位置和目录中的类,但是,ClassLoader被设计成了一种委托模式,使得某一个ClassLoader可以委托它的父级类装载器去加载类,从而让应用程序可以借助某一个子级的ClassLoader去多个位置和目录中进行类的加载。
对于一个类,一个类装载器本身可能不能装载它,但是只要它的父类装载器中有可以装载这个类的,那么这个类装载器就可以把这个类委托给它自己的父类装载。
类加载的过程
ClassLoader的loadClass方法先查找这个类是否已被加载,如果没有加载则委托其父级类装载器去加载这个类,如果父级的类装载器无法装载这个类,子级类装载器才调用自己内部的findClass方法去进行真正的加载。
委托过程会一直追溯到BootStrap类装载器,如果委托过程中的所有类装载器都不能完成类的装载,最终就会报告ClassNotFoundException异常。
通过中间变量交换两个变量的值
Void swap(int *a, int *b)
{
int *t = a;
*a = *b;
*b = t;
}
在不定义中间变量的情况下交换两个变量的值
Void swap(int *a, int *b)
{
*a = *a + *b; //加法可能溢出
*b = *a - *b;
*a = *a - *b;
}
Void swap(int *a, int *b)
{
*a = (*a) * (*b); //乘法可能溢出
*b = (*a) / (*b); //除法可能有分母为0的问题
*a = (*a) / (*b);
}
Void swap(int *a, int *b)c
{
*a = (*a) ^ (*b);
*b = (*a) ^ (*b);
*a = (*a) ^ (*b);
}
下面取地址操作是否可行,为什么
- int** a=&&b;
- int *a=&3;
- int *a=&go();
- &b取出来的地址放CPU寄存器,不可取&b只能去内存地址
- 3是常量,不可用&取址只有变量才有内存地址
- go()是函数返回值,在被赋予变量前被放在CPU寄存器
C语言内存管理
内存空间 | 读写权限 |
---|---|
kernel | 不可读写 |
环境变量与命令参数 | 可读不可写 |
栈空间 | 可读可写 |
共享库动态库 | 可读不可写 |
堆空间 | 申请后可读写 |
BSS区(初始为0) | 可读可写 |
Data区 | 可读可写 |
只读区 | 可读不可写 |
代码区 | 可读不可写 |
写出下列代码中变量的位置
全局变量和任何static变量都在全局区,这些变量赋值就在Data区,不赋值就在BSS区
int a=3; //全局区的Data区
int b; //全局区的BSS区
static int c=4; //全局区的Data区
static int d; //全局区的BSS区
int main(void)
{
int e; //栈空间
int f=5; //栈空间
static int g; //全局区的BSS区
static int h = 6; //全局区的Data区
if(1)
{
int i=7; //栈空间
}
char *p=malloc(10); //p在栈空间 malloc(10)在堆空间
char *str="hello"; //str在栈空间 "hello"在只读区
}
单例模式和静态方法的区别
-
静态方法性能更好,在编译期就已经绑定好了
-
单例模式可以延迟初始化,静态方法在第一次使用时初始化。如果需要加载比较重的对象,用单例模式会更好
-
单例模式可以被继承,方法可以被重写,静态方法不行
单子模式适用场景
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多
单子模式的三要素
-
私有静态属性,用于存取类的唯一实例。
-
公共静态方法,用于提供对该唯一实例的存取访问,如果实例未创建,则创建该实例 。
-
私有构建函数,用于限制类再次实例化的方式。
单例模式的优点
- 在内存中只有一个对象,节省内存空间
- 避免频繁的创建销毁对象,可以提高性能
- 避免对共享资源的多重占用。
- 可以全局访问
单子模式可不可以new出多个实例?
可以,用反射技术
单子模式示例代码
/*
饿汉模式:“比较勤”,实例在初始化的时候就已经建好了,
不管有没有用到,都先建好了再说。
好处是没有线程安全的问题,坏处是浪费内存空间
*/
public class Singleton {
//1 私有构造器 外部不能访问
private Singleton(){
}
//2 声明一个静态的自己的类
private static Singleton singleton = new Singleton();
//3 返回一个返回自己类的方法
public static Singleton getSingleton(){
return singleton;
}
}
/*
懒汉模式:实例在用到的时候才去创建,“比较懒”,
用的时候才去检查有没有实例,如果有则返回,没有则新建
线程不安全,严格意义上不是不是单例模式
*/
public class Singleton{
//1 私有构造器 外部不能访问
private Singleton(){
}
//2 声明一个静态的自己的类
private static Singleton singleton = null;
//3 返回一个返回自己类的方法
public static Singleton getSingleton() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
实现多线程的方法
- 继承Thread类
- 实现Runnable接口
什么是死锁
不同的线程都在等待那些根本不可能被释放的锁,从而导致没有外力作用时无法推进,所有的工作都无法完成。
为什么会发生死锁
竞争资源、进程推进顺序不当
JavaBean定义
JavaBean是一种 JAVA 语言写成的可重用组件。JavaBean 通过提供符合一致性设计模式的 公共方法将内部域暴露成员属性,set和get方法获取。JavaBean定义了一组规则JavaBean就是遵循此规则的平常的Java对象
JavaBean满足这三个条件:
-
执行java.io.Serializable接口
-
提供无参数的构造器
-
提供getter和setter方法访问它的属性.
Javabean可以根据get和set方法推断出类内部私有变量的名字
JavaBean示例代码
/*
老师的例子
*/
public class Point {
private String name;
private int age = 20;
public Point(String name, int age) {
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;
}
}
基本内置注解
@Override
覆写 函数名和参数必须和父类一样 重写父类的方法
@Deprecated
弃用
@SuppressWarnings
抑制警告
Java异常类层次结构图
Error和Exception 的区别
堆和栈
栈空间:
- 由编译器自动分配释放,存放函数的参数值,局部变量的值等;
- 操作方式类似于数据结构中的栈,占用连续存储空间;
- 栈空间向着内存地址减小的方向消耗空间;
- 栈空间的大小一般是2M,从栈获得的空间较小;由计算机底层的支持,压栈和出栈都有专门的指令,效率较高。
堆空间:
- 由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收;
- 操作方式类似链表,更新频繁,造成可用空间碎片化,每块可用空间都很小;
- 堆获得的空间比较灵活,也比较大,向着内存地址增大的方向消耗空间;
- 堆空间通过可用空间链表的扫描、合并等操作动态的获取空间,效率相对较低。
下面代码的区别
int num = 10;
//局部变量num保存在stack中,stack一般1M
String str = "hello";
//str在栈中,保存的是一个引用,指向"hello"首字母;
//"hello"在堆中,底层是为hello做一次malloc
如果我们做如下修改,内存中发生了什么?
Num=20;
//栈中10变20 (20覆盖原来的10)
Str="nuist";
//java在内存(堆)中重新开辟一个区域(malloc)存放nuist,并且str指向这个新的字符串。
//原来的hello字符串会被java垃圾回收器回收(free)