java基础

来源:互联网 发布:教务系统的数据库设计 编辑:程序博客网 时间:2024/05/17 22:24

java 基础
一、 基础部分(1)
1.1、String对象创建问题
String str=”abc”;
毫无疑问,这行代码创建了一个String对象。
String a=”abc”; String b=”abc”; 那这里呢?
答案还是一个。
String a=”ab”+”cd”; 再看看这里呢?
答案是三个。
原因:在JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象,并且可以被共享使用,因此它提高了效率。由于String类是final的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱。字符串池由String类维护,我们可以调用intern()方法来访问字符串池。
因此用上面这种方式创建String对象的时候,其实就是到字符串池里面去拿,如果有这个对象直接拿,没有就创建这个对象然后拿,所有不管定义几个变量a、b、c都只创建了一个对象。但是第三个的时候,用到了三个对象’ab’,’cd’和’abcd’所以创建了三个对象。

另外一种创建String的方法:
String a = new String(“123”);
这样的形式就被当作对象创建在堆中,而没有字符串池什么事情了,每一次的String创建都相当于创建了一个新的对象,而不同对象用==可以想象会是false。

1.2、随机创建一个字符串
随机创建一个字符串并截取前面7位
import java.util.UUID;
public static String getUUid(){
return UUID.randomUUID().toString().substring(0,7);
}
1.3、随机创建一个数字
这里写图片描述
1.4进制转换
10进制转2进制
这里写图片描述
1.5 Session和cookies区别
大公司面试官问到的cookie和session的区别。你知道吗
1.cookie产生
识别用户
HTTP是无状态协议,这就回出现这种现象:当你登录一个页面,然后转到登录网站的另一个页面,服务器无法认识到。或者说两次的访问,服务器不能认识到是同一个客户端的访问,这就让你重复登录,所以产生了cookie。
cookie:第一次访问一个服务器,不携带cookie,这时服务器在响应(response)下行HTTP报文中,命令浏览器携带cookie信息;浏览器再访问同一个域的时候,将把cookie信息携带到请求(request)上行HTTP请求中,从而实现了HTTP模拟有了状态。
2.cookie特点
cookie是不加密的
cookie是可以被篡改和攻击
cookie大小受到限制
3.node使用
//使用cookie必须要使用cookie-parser中间件
var cookieParser = require(‘cookie-parser’);
app.use(cookieParser());
express中的cookie: res负责设置cookie, req负责识别cookie。
cookie用来制作记录用户的一些信息,如购买历史、猜你喜欢、访问量等
现在看session
4.session产生
由于cookie明文等一些不足所以产生了session
session依赖cookie,就是利用cookie,实现的“会话”,因此当cookie被禁用,session也无法使用。
session比cookie不一样在哪里呢? session会下发一个秘钥(cookie)(乱码),客户端每次访问都携带这个秘钥,那么服务器如果发现这个秘钥吻合,就能够显示这个用户曾经保存的信息。
任何语言中,session的使用,是“机理透明”的,也就是让你感觉不到这事儿和cookie有关
5.session特点和使用
session是加密的
var session = reqiure(“express-session”);
app.use(session({
..一些配置
}));
app.get(“/”,function(req,res){
console.log(req.sission.login);
});
app.get(“/login”,function(req,res){
req.session.login = “1”;
});
都是req对象
Session存在于服务器的内存中,如果服务器重启就会丢失session同时需要重新登录
6.cookie和session不同
cookie数据存放在客户的浏览器上;session数据放在服务器缓存中。
cookie是明文,不安全,别人利用cookie可以被篡改和攻击;而session存放服务器缓存中并且加密的,其他用户看不到。
session会在一定时间内保存在服务器上。当用户访问增多,会比较占用你服务器内存,考虑到减轻服务器性能方面,使用cookie。
单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie;session的密钥(cookie),可以对应可以对应无限大的数据

1.6Java中类是如何加载的
所有的类都是第一次使用的时候,动态加载到JVM中。创建对类的静态成员的引用,加载这个类。Java程序在开始运行的时候并非完全加载,类都是用的地方在加载,这就是动态加载

首先检查这个类是否被加载
如果没有加载,再去根据类名查找.class文件,加载类的字节码,并校验是否存在不良代码
1.7 动态代理
1、jdk创建动态代理
这里写图片描述
*2、cglib创建动态代理*
1、创建一个要代理的类
这里写图片描述
2、创建代理类的核心
这里写图片描述
3.测试结果
这里写图片描述
主要应用场景
1. 在操作前后做日志记录
2. 测试方法执行时间

1.8 JAVA中堆和栈的区别
1、栈和堆
栈:在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配
当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

堆:堆内存用来存放由new创建的对象和数组
2、java中变量在内存中的分配
1、类变量(static修饰的变量):在程序加载时系统就为它在堆中开辟了内存,堆中的内存地址存放于栈以便于高速访问。静态变量的生命周期–一直持续到整个”系统”关闭

2、实例变量:当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量(比如说类实例),然后根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的”物理位置”。 实例变量的生命周期–当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存

3、局部变量:局部变量,由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放
3、java的内存机制
Java 把内存划分成两种:一种是栈内存,另一种是堆内存。

  在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。

  堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。

这也是 Java 比较占内存的原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!
4、区别
1、申请方式和回收方式不同
堆和栈的第一个区别就是申请方式不同:栈(英文名称是stack)是系统自动分配空间的,例如我们定义一个 char a;系统会自动在栈上为其开辟空间。而堆(英文名称是heap)则是程序员根据需要自己申请的空间,例如malloc(10);开辟十个字节的空间。
由于栈上的空间是自动分配自动回收的,所以栈上的数据的生存周期只是在函数的运行过程中,运行后就释放掉,不可以再访问。而堆上的数据只要程序员不释放空间,就一直可以访问到,不过缺点是一旦忘记释放会造成内存泄露。
2、申请效率的比较
根据第0点和第1点可知
栈:由系统自动分配,速度较快。但程序员是无法控制的。
堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。
3.堆栈空间分配
①栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
②堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
4.堆栈缓存方式
①栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
②堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
5.堆栈数据结构区别
①堆(数据结构):堆可以被看成是一棵树,如:堆排序。
②栈(数据结构):一种先进后出的数据结构。

1.9、session中的存值和取值
reuqest.getSession().setAttribute(“user”,user);这是存值,存入的是一个user对象
Users user = (Users) request.getSession().getAttribute(“user”); 这是取值。取出这个user对象
user.getUserName(); 获取user对象的username

2.0、ArrayList
1、基础知识
public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable
ArrayList 是一个数组队列,相当于 动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。
ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。稍后,我们会比较List的“快速随机访问”和“通过Iterator迭代器访问”的效率。
ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
和Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
2、ArrayList遍历方式
1、通过迭代器遍历。即通过Iterator去遍历。
String str=null;
Iterator iter = list.iterator();
while (iter.hasNext()) { //判断iter是否还有下一个
str = (String) iter.next(); //获取序列中的下一个元素
System.out.println(str);
}
这里写图片描述
2、第二种,for循环遍历。
String str=null;
for (Object list:lists) {
str = (String) list;
System.out.println(str);
}
这里写图片描述

3、随机访问,通过索引值去遍历。
这里写图片描述
4、三种方法效率排行
遍历ArrayList时,使用随机访问(即,通过索引序号访问)效率最高,而使用迭代器的效率最低!
3、增加和删除机制
增加和删除方法主要需要特别关心的就两个地方:1.数组扩容(先判断时候会超出,如果超出就进行扩容),2.数组复制,这两个操作都是极费效率的,最惨的情况下(添加到list第一个位置,删除list最后一个元素或删除list第一个索引位置的元素)时间复杂度可达O(n)。
4、ArrayList和Vector的区别
1.Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类(线程安全的)。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。
2.Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。
3.Vector还有一个子类Stack.
2.1、LinkedList源码解析
LinkedList与ArrayList一样实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口链表的实现。基于链表实现的方式使得LinkedList在插入和删除时更优于ArrayList,而随机访问则比ArrayList逊色些。
1、LinkedList是一种链表结构
LinkedList是基于链表结构的一种List,在分析LinkedList源码前有必要对链表结构进行说明。
1.链表的概念
链表是由一系列非连续的节点组成的存储结构,简单分下类的话,链表又分为单向链表和双向链表,而单向/双向链表又可以分为循环链表和非循环链表,下面简单就这四种链表进行图解说明。
1.1.单向链表
单向链表就是通过每个结点的指针指向下一个结点从而链接起来的结构,最后一个节点的next指向null。
这里写图片描述
1.2.单向循环链表
单向循环链表和单向列表的不同是,最后一个节点的next不是指向null,而是指向head节点,形成一个“环”
这里写图片描述
1.3.双向链表
从名字就可以看出,双向链表是包含两个指针的,pre指向前一个节点,next指向后一个节点,但是第一个节点head的pre指向null,最后一个节点的tail指向null。
这里写图片描述
1.4.双向循环链表
双向循环链表和双向链表的不同在于,第一个节点的pre指向最后一个节点,最后一个节点的next指向第一个节点,也形成一个“环”。而LinkedList就是基于双向循环链表设计的。
这里写图片描述
2、API方法摘要
这里写图片描述
这里写图片描述
2.2、ArrayList和LinkedList比较
这里写图片描述

1、顺序插入速度ArrayList会比较快,因为ArrayList是基于数组实现的,数组是事先new好的,只要往指定位置塞一个数据就好了;LinkedList则不同,每次顺序插入的时候LinkedList将new一个对象出来,如果对象比较大,那么new的时间势必会长一点,再加上一些引用赋值的操作,所以顺序插入LinkedList必然慢于ArrayList
2、基于上一点,因为LinkedList里面不仅维护了待插入的元素,还维护了Entry的前置Entry和后继Entry,如果一个LinkedList中的Entry非常多,那么LinkedList将比ArrayList更耗费一些内存
3、数据遍历的速度,看最后一部分,这里就不细讲了,结论是:使用各自遍历效率最高的方式,ArrayList的遍历效率会比LinkedList的遍历效率高一些
4、有些说法认为LinkedList做插入和删除更快,这种说法其实是不准确的:
(1)LinkedList做插入、删除的时候,慢在寻址,快在只需要改变前后Entry的引用地址
(2)ArrayList做插入、删除的时候,慢在数组元素的批量copy,快在寻址
2.3、List总结
• ArrayList以数组形式实现,顺序插入、查找快,插入、删除较慢
• LinkedList以链表形式实现,顺序插入、查找较慢,插入、删除方便
2.4、HashMap
它是基于哈希表的 Map 接口的实现,以key-value的形式存在。
HashMap继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。
2.5、HashMap和Hashtable的区别
1.两者最主要的区别在于Hashtable是线程安全,而HashMap则非线程安全
Hashtable的实现方法里面都添加了synchronized关键字来确保线程同步,因此相对而言HashMap性能会高一些,我们平时使用时若无特殊需求建议使用HashMap,在多线程环境下若使用HashMap需要使用Collections.synchronizedMap()方法来获取一个线程安全的集合(Collections.synchronizedMap()实现原理是Collections定义了一个SynchronizedMap的内部类,这个类实现了Map接口,在调用方法时使用synchronized来保证线程同步,当然了实际上操作的还是我们传入的HashMap实例,简单的说就是Collections.synchronizedMap()方法帮我们在操作HashMap时自动添加了synchronized来实现线程同步,类似的其它Collections.synchronizedXX方法也是类似原理)
2.HashMap可以使用null作为key,而Hashtable则不允许null作为key
虽说HashMap支持null值作为key,不过建议还是尽量避免这样使用,因为一旦不小心使用了,若因此引发一些问题,排查起来很是费事
3.HashMap以null作为key时,总是存储在table数组的第一个节点上
4.HashMap是对Map接口的实现,HashTable实现了Map接口和Dictionary抽象类
5.HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75
6.HashMap扩容时是当前容量翻倍即:capacity2,Hashtable扩容时是容量翻倍+1即:capacity2+1
这里写图片描述
这里写图片描述
API方法摘要

2.List、map、set思维导图
这里写图片描述
2.5Java反射机制定义
Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

Java反射相关操作
前面我们知道了怎么获取Class,那么我们可以通过这个Class干什么呢?
总结如下:
获取成员方法Method
获取成员变量Field
获取构造函数Constructor
我们只要知道这个类的路径全称就能玩弄它于鼓掌之间。
1、反射的使用
Class c = Class.forName(“com.tengj.reflect.Person”); //首先生成class
Object o = c.newInstance(); //newInstance可以初始化一个实例
Method method = c.getMethod(“fun”, String.class, int.class);//获取方法
method.invoke(o, “tengj”, 10); //通过invoke调用该方法,参数第一个为实例对象,后面为具体参数值
这里写图片描述
这里写图片描述
这里写图片描述

2.6、多线程
这里写图片描述
2.7 设计模式
1. 简单工厂模式
这里写图片描述
工厂类(Factory)角色:担任这个角色的是工厂方法模式的核心,含有与应用紧密相关的商业逻辑。
抽象产品(Product)角色:担任这个角色的类是由工厂方法模式所创建的对象的父类,或它们共同拥有的接口,这里指的就是Pizza这个类。
具体产品(Concrete Product)角色:工厂方法模式所创建的任务对象都是这个角色的实例,这里指GreekPizza和CheesePizza。
这里写图片描述
这里写图片描述

缺点:简单工厂模式的时候提到它对开闭原则支持的不够,因为如果有新的产品加入到系统中去,就需要修改工厂类,就违反了开闭原则了,这次介绍的工厂方法模式在保持简单工厂模式优点的前提下,还满足了开闭原则,关键在于它的多态性。
2、工厂方法模式
这里写图片描述
抽象工厂角色:担任这个角色的是工厂方法模式的核心,它是与应用程序无关的。任何在模式中创建对象的工厂类必须实现这个接口。
具体工厂角色:担任这个角色的是实现了抽象工厂接口的具体Java类,具体工厂角色含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
抽象产品角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
具体产品角色:这个角色实现了抽象产品角色所申明的接口。工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

3.工厂方法模式和简单工厂模式比较:
工厂方法模式跟简单工厂模式在结构上的不同是很明显的,工厂方法模式的核心是一个抽象工厂类,而简单工厂模式的核心在一个具体类。显而易见工厂方法模式这种结构更好扩展,权力下发,分布式比集中式更具优势。

如果系统需要加入一个新的产品,那么所需要的就是向系统中加入一个这个产品类以及它所对应的工厂类。没有必要修改客户端,也没有必要修改抽象工厂角色或者其他已有的具体工厂角色。对于增加新的产品类而言,这个系统完全支持开闭原则。
4、单例模式
1、单例模式概念
单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
2、单例模式有三个特性:
单例类只能有一个实例
单例类必须自行创建自己的唯一的实例
单例类必须给所有其他对象提供这一实例