基础
String s = new String("xyz"); 创建了几个 String Object?
两个;常量池中只可能只有一个“xyz”,但是堆中可能有多个,因为你这里用了new String来强制在堆中再创建了一个对象,所以是两个哈;如果是String s= "xyz"这样的,s这个引用就会直接指向常量池的"xyz",而不会是指向你在堆中创建的“xyz”。
List和Set的区别
List:
- 可以允许重复的对象。
- 可以插入多个null元素。
- 是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
- 常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
Set:
- 不允许重复对象
- 无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。
- 只允许一个 null 元素
- Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。
HashMap和HashTable的区别
HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。
1.HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
2.HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
3.HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
4.由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
5.HashMap不能保证随着时间的推移Map中的元素次序是不变的。
大佬的详细讲解
Map集合如何遍历
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class TestMap {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "a");
map.put(2, "b");
map.put(3, "ab");
map.put(4, "ab");
map.put(4, "ab");// 和上面相同 , 会自己筛选
System.out.println(map.size());
// 第一种:
/*
* Set<Integer> set = map.keySet(); //得到所有key的集合
*
* for (Integer in : set) { String str = map.get(in);
* System.out.println(in + " " + str); }
*/
System.out.println("第一种:通过Map.keySet遍历key和value:");
for (Integer in : map.keySet()) {
//map.keySet()返回的是所有key的值
String str = map.get(in);//得到每个key多对用value的值
System.out.println(in + " " + str);
}
// 第二种:
System.out.println("第二种:通过Map.entrySet使用iterator遍历key和value:");
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
// 第三种:推荐,尤其是容量大时
System.out.println("第三种:通过Map.entrySet遍历key和value");
for (Map.Entry<Integer, String> entry : map.entrySet()) {
//Map.entry<Integer,String> 映射项(键-值对) 有几个方法:用上面的名字entry
//entry.getKey() ;entry.getValue(); entry.setValue();
//map.entrySet() 返回此映射中包含的映射关系的 Set视图。
System.out.println("key= " + entry.getKey() + " and value= "
+ entry.getValue());
}
// 第四种:
System.out.println("第四种:通过Map.values()遍历所有的value,但不能遍历key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}
}
什么场景下使用list,set,map呢?
(或者会问为什么这里要用list、或者set、map,这里回答它们的优缺点就可以了)
- 如果你经常会使用索引来对容器中的元素进行访问,那么 List 是你的正确的选择。如果你已经知道索引了的话,那么 List 的实现类比如 ArrayList 可以提供更快速的访问,如果经常添加删除元素的,那么肯定要选择LinkedList。
- 如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是 List,因为 List 是一个有序容器,它按照插入顺序进行存储。
- 如果你想保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个 Set 的实现类,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的实现类都遵循了统一约束比如唯一性,而且还提供了额外的特性比如 TreeSet 还是一个 SortedSet,所有存储于 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 进行排序。LinkedHashSet 也按照元素的插入顺序对它们进行存储。
- 如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。
算法
根据给定数组形成新乱序数组
public class Main {
public static void main(String[] args) {
int[] a ={1,2,3,4,5,6,7,8,9,10};
int[] b = new int[a.length];
for (int i = 0;i<a.length;i++){
//随机生成下标范围依次减少1,又因为取出的元素会被从队尾依次往前放置,这样就能保证 不会再产生已经产生过的元素。
int rand = (int)(Math.random()*(a.length-i));
b[i] = a[rand];
//将取出的元素从队尾依次往前放置
int x = a[a.length-1-i];
a[a.length-1-i] = a[rand];
a[rand] = x;
}
//字符串输出
System.out.println(Arrays.toString(a));
System.out.println(Arrays.toString(b));
}
}
单链表的逆置
递归搞法
public class Recursion {
class Node{
private int value;
private Node nextNode;
Node(int value){
this.value = value;
}
}
/**
* 逆置单链表(递归)
* @param head 头结点
* @return 逆置后的头结点
*/
private static Node revert(Node head) {
if (head == null || head.nextNode == null) {
// 到达尾结点
return head;
}
//入栈
Node revertHead = revert(head.nextNode);
//出栈
head.nextNode.nextNode = head;
head.nextNode = null;
//返回尾节点
return revertHead;
}
}
迭代搞法
public class iteration {
private class Node {
public int value;
public Node nextNode;
public Node(int value) {
this.value = value;
}
}
public static Node revertIter(Node head){
Node pre = head;
Node cur = head.nextNode;
Node tmp;
//头结点的nextNode应该要置空
pre.nextNode = null;
while (cur != null){
//先放next节点
tmp = cur.nextNode;
//修改next节点指向pre
cur.nextNode = pre;
pre = cur;
cur = tmp;
}
return pre;
}
}
二叉树排序的基本原理
使用第一个元素作为根节点,如果之后的元素比第一个小,则放到左子树,否则放到右子树,之后按中序遍历。
下面实现一个二叉树排序的比较算法,为了操作方便,使用Integer类完成。
public final class Integer extends Number implements Comparable<Integer>
我们可以看到Integer类实现了Comparable接口,所以可用Integer实例化Comparable接口对象
public class BinaryTree {
class Node{ //声明一个节点类
private Comparable data; //节点的数据类型为Comparable
private Node left; //保存左子树
private Node right; //保存右子树
public Node(Comparable data){ //构造函数
this.data= data;
}
public void addNode(Node newNode){
//确定是放在左子树还是右子树
if (newNode.data.compareTo(this.data)<0){ //新节点值小于当前节点
if (this.left == null){
this.left = newNode;//左子树为空的话,新节点设为左子树
}else {
this.left.addNode(newNode); //否则继续向下判断
}
}else{ //新节点的值大于或者等于当前节点
if (this.right == null){
this.right = newNode;
}else {
this.right.addNode(newNode);
}
}
}
public void printNode(){ //采用中序遍历
if (this.left != null){ //如果不为空先输出左子树
this.left.printNode();
}
System.out.print(this.data+"\t"); //输出当前根节点
if (this.right != null){ //输出右子树
this.right.printNode();
}
}
}
private Node root; //表示根元素
public void add(Comparable data){ //向二叉树中插入元素
Node newNode =new Node(data);
if (root == null){ //没有根节点
root = newNode;
}else {
root.addNode(newNode); //判断放在左子树还是右子树
}
}
public void print(){
root.printNode(); //根据根节点输出
}
}
多线程
给一个整数i要求建立四个线程两个加,两个减,各100次。
public class ManyThread {
//采用Runnable接口方式创建的多条线程可以共享实例属性
private int i;
//同步增加方法
private synchronized void inc(){
i++;
System.out.println(Thread.currentThread().getName() + "--inc--" + i);
}
//同步减计算
private synchronized void dec(){
i--;
System.out.println(Thread.currentThread().getName() + "--dec--" + i);
}
//增加线程,非静态内部类
class Inc implements Runnable{
@Override
public void run() {
int i = 0;
while (i++ <100){
inc();
}
}
}
//减算线程 非静态内部类
class Dec extends Thread{
@Override
public void run() {
int i =0;
while (i++ < 100){
dec();
}
}
}
public static void main(String[] args) {
// 由于内部类是非静态的,所以这样需要Test的实例化才能调用生成内部类实例
ManyThread t = new ManyThread();
// 内部类的实例化
Inc inc = t.new Inc(); //
// Dec dec = t. new Dec();
Thread thread = null;
// 创建 2 个增加线程
for (int i = 0; i < 2; i++) {
thread = new Thread(inc); // 实现Runnable的类的实例化,使用带参数的Thread构造方法.
thread.start();
}
// 创建 2 个减少线程
for (int i = 0; i < 2; i++) {
thread = t.new Dec(); // 继承Thread的类可以直接实例化.
thread.start();
}
}
}
数据库
Mysql的优化
大体可以分为三部分:索引的优化,sql语句的优化,表的优化
1、索引的优化
只要列中含有NULL值,就最好不要在此例设置索引,复合索引如果有NULL值,此列在使用时也不会使用索引
尽量使用短索引,如果可以,应该制定一个前缀长度对于经常在where子句使用的列,最好设置索引,这样会加快查找速度
对于有多个列where或者order by子句的,应该建立复合索引
对于like语句,以%或者‘-’开头的不会使用索引,以%结尾会使用索引
尽量不要在列上进行运算(函数操作和表达式操作)
尽量不要使用not in和<>操作
2、sql语句的优化
查询时,能不要就不用,尽量写全字段名
大部分情况连接效率远大于子查询
多使用explain和profile分析查询语句
查看慢查询日志,找出执行时间长的sql语句优化
多表连接时,尽量小表驱动大表,即小表 join 大表
在千万级分页时使用limit
对于经常使用的查询,可以开启缓存
3、表的优化
表的字段尽可能用NOT NULL
字段长度固定的表查询会更快
把数据库的大表按时间或一些标志分成小表
将表分区
购物车数据放在哪里?(三种)
目前我们使用购物车的存储方式主要有:Session方式,Cookie方式,数据库存储,我们来一一分析优缺点。
1、Session(Memcached)方式
优点:购物车信息保存在服务端,可以保存1M 信息。
缺点:对于大型网站会占有过多的服务器内存资源,造成服务器压力过大。Session保存的信息会在用户退出登录后丢失。用户下次登录,购物车中商品信息丢失,用户只能从新选择。
2、Cookie方式
优点:购物车信息存储在客户端,不占用服务器资源,基本可以到达持久化存储。
缺点:Cookie有大小的限制,不能超过4K,而且不够安全。如果是个人PC机,Cookie能很好的保存购物车信息,但如果是公共办公环境,Cookie保存的信息基本就失效了(会被其他人购物车信息覆盖)。对一个大型的电子商务网站,我们需要对用户的购买行为进行分析,需要对用户推荐用户感兴趣的商品,如果把购物车信息保存在Cookie中,则不能对用户购买行为分析统计。
3、 数据库存储
优点:持久化存储,可以分析用户购买行为。
缺点: 网站速度变慢,成本和维护增加。
面向对象
有两个篮子分别为A和B,篮子A装着鸡蛋,篮子B装着苹果,请用面向对象的思路实现两个篮子里的物品交换。
public class Basket {
private String s;
public Basket(){
super();
}
public Basket(String s){
super();
this.s = s;
}
public String getS() {
return s;
}
public void getS(String s){
this.s = s;
}
public String toString(){
return "Basket [s=" + s +"]";
}
public static void main(String[] args) {
Basket A = new Basket("egg");
Basket B = new Basket("apple");
String gA = A.getS();
String gB = B.getS();
System.out.println(gA+":"+gB);
A.getS(gB);
B.getS(gA);
String gA2 = A.getS();
String gB2 = B.getS();
System.out.println(gA2+":"+gB2);
}
}
本文由 一去二三遥 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Feb 7, 2020 at 06:57 pm
这是高手
我来顶贴了
哈哈,感谢,都是些很基础的面试题,经常查漏补缺
嗯~想想还是修图来得简单啊~
以此为业能混口饱饭的都不容易,修图不也得把PS那些玩转么,专业不同罢了,都辛苦
我也想学学代码 想自己搞个主题 不过感觉都没有啥时间去学这个了 你的主题挺简洁的 好看!
这主题是大佬写的,之前也有搞主题的想法,但是审美达不到自己想要的哪种效果就放弃了,想学代码可以啊,搞网站主题就学学前端内容,PHP啥的比较适合
抓哇啊
转抓哇,防脱发