Java雷池-整理向
in 代码 with 6 commentsand 684 read

Java雷池-整理向

in 代码 with 6 commentsand 685 read

最近重拾Java,一开始想头铁一下子进钻ssm框架,发现一年多不碰Java,一些基础和细节一下子还捡不起来,写起代码来磕磕碰碰,于是就花了一周的时间重新走了一遍java之路。一走吓一跳,发现了很多从前都没有意识到的问题所在,也许最近一直浸淫在C++的使用习惯里,从头来发现了很多或因历史原因或因语言特性而不一样或者不是很合理的地方。总结整理出来,每天一看,也好警示自己。不跳雷池。(PS,知识点比较零散,基本一段一个知识点,因为太多只是大致归了一下类,如果有疑惑或者纠出错来,务必留言,定予以解答和改正

基础语法

Static

首先声明什么是静态方法:
1.用static修饰的方法
2.静态方法是使用公共内存空间的,就是说所有对象都可以直接引用,不需要创建对象再使用该方法。
其次说明静态方法的使用:
1.在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。
2.而实例方法只有后面这种方式。
也就是说,只有调用静态方法时可以无需创建对象。
根据题目.
1:AB为一个类,可以不创建对象,直接使用AB.method ("类名.方法名"的方式)
---------------所以method是static修饰的静态方法
2:其次method无返回值
----------------所以method是void类型的方法.

"=="与"equals"

1、基本型和基本型封装型进行“==”运算符的比较,基本型封装型将会自动拆箱变为基本型后再进行比较,因此Integer(0)会自动拆箱为int类型再进行比较,显然返回true;

     int a = 220;
     Integer b = 220;
    System.out.println(a==b);//true

2、两个Integer类型进行“==”比较, 如果其值在-128至127 ,那么返回true,否则返回false, 这跟Integer.valueOf()的缓冲对象有关,这里不进行赘述。

    Integer c=3;
    Integer h=3;
    Integer e=321;
    Integer f=321;
    System.out.println(c==h);//true
    System.out.println(e==f);//false

3、两个基本型的封装型进行equals()比较,首先equals()会比较类型,如果类型相同,则继续比较值,如果值也相同,返回true。

    Integer a=1;
    Integer b=2;
    Integer c=3;
    System.out.println(c.equals(a+b));//true

4、基本型封装类型调用equals(),但是参数是基本类型,这时候,先会进行自动装箱,基本型转换为其封装类型,再进行3中的比较。

    int i=1;
    int j = 2;
    Integer c=3;
    System.out.println(c.equals(i+j));//true

&& 和 || 为短路与 短路或

&&若前面的表达式为false,整个逻辑表达式为false,所以后面的表达式无论true和false都无法影响整个表达式的逻辑结果,所以为了提高代码执行速率,这里后面的表达式就不会执行。
同理,若前面表达式为true,则后面的表达式无需计算。
& 和 | 为不短路与 不短路或
无论什么情况,前面的和后面的都要执行。

A.java用来运行一个.class文件
B.javadoc用来生成api文档
C.jar用来生成jar包
D.javac用来把.java文件编译为.class文件

在Java中,使用字符串对char数组赋值,必须使用toCharArray()方法进行转换。

count = count++

这句话在Java和C++里面,运行结果是不一样的。
Java中等价于:tmp = count;count++;count = tmp;
C++中等价于:tmp = count;count = tmp;count++;

long → float 无须强制转换(这个选项最容易出错

float占4个字节为什么比long占8个字节大呢,因为底层的实现方式不同。
浮点数的32位并不是简单直接表示大小,而是按照一定标准分配的。
第1位,符号位,即S
接下来8位,指数域,即E。
剩下23位,小数域,即M,取值范围为[1 ,2 ) 或[0 , 1)
然后按照公式: V=(-1)^s * M * 2^E
也就是说浮点数在内存中的32位不是简单地转换为十进制,而是通过公式来计算而来,通过这个公式虽然,只有4个字节,但浮点数最大值要比长整型的范围要大。
String参与运算优先级

Java8

JDK中提供了三个ClassLoader,根据层级从高到低为:

  1. Bootstrap ClassLoader,主要加载JVM自身工作需要的类。
  2. Extension ClassLoader,主要加载%JAVA_HOME%libext目录下的库类。
  3. Application ClassLoader,主要加载Classpath指定的库类,一般情况下这是程序中的默认类加载器,也是ClassLoader.getSystemClassLoader() 的返回值。(这里的Classpath默认指的是环境变量中配置的Classpath,但是可以在执行Java命令的时候使用-cp 参数来修改当前程序使用的Classpath)

Lambda表达式的主要作用就是代替匿名内部类的繁琐语法, 它由三部分组成:
(1) 形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。
(2) 箭头(→)。必须通过英文中画线和大于符号组成。
(3)代码块。如果代码块只包含一条语句,Lambda表达式允许省略代码块的花括号,那么那条语句就不要用花括号表示语句结束。Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而它的代码块中仅有一套省略了return的语句。Lambda表达式会自动返回这条语句的值。

各个修饰符的权限:
权限

阿里巴巴笔试题
以下代码的输出结果是?

public class B
{
    public static B t1 = new B();
    public static B t2 = new B();
    {
        System.out.println("构造块");
    }
    static
    {
        System.out.println("静态块");
    }
    public static void main(String[] args)
    {
        B t = new B();
    }
}

答案 :构造块,构造块,静态块,构造块
开始时JVM加载B.class,对所有的静态成员进行声明,t1 t2被初始化为默认值,为null,又因为t1 t2需要被显式初始化,所以对t1进行显式初始化,初始化代码块→构造函数(没有就是调用默认的构造函数),咦!静态代码块咋不初始化?因为在开始时已经对static部分进行了初始化,虽然只对static变量进行了初始化,但在初始化t1时也不会再执行static块了,因为JVM认为这是第二次加载类B了,所以static会在t1初始化时被忽略掉,所以直接初始化非static部分,也就是构造块部分(输出''构造块'')接着构造函数(无输出)。接着对t2进行初始化过程同t1相同(输出'构造块'),此时就对所有的static变量都完成了初始化,接着就执行static块部分(输出'静态块'),接着执行,main方法,同样也,new了对象,调用构造函数输出('构造块')

网友onlywu解析:
之前我一直有一个误区!就是认为静态块一定是最先初始化的!但是,阿里爸爸今天又用一记重拳猛击我的脸,额,好疼....当时的情况是这样的:
我在牛客网找虐中,碰到了这样的一道题,心中充满了鄙夷,心想"这tm还用看吗,肯定先是静态块,再接着三个构造块,弱鸡题",但是 = =
,答案却是"构造块 构造块 静态块 构造块". ......[黑线|||||||||] 于是总结了一下,以警后世 - -
正确的理解是这样的: 并不是静态块最先初始化,而是静态.(BM:啊!多么痛的领悟!) 而静态域中包含静态变量、静态块和静态方法,其中需要初始化的是静态变量和静态块.而他们两个的初始化顺序是靠他们俩的位置决定的! So!
初始化顺序是 t1 t2 静态块

识别合法的构造方法

1:构造方法可以被重载,一个构造方法可以通过this关键字调用另一个构造方法,this语句必须位于构造方法的第一行;

重载:方法的重载(overload):重载构成的条件:方法的名称相同,但参数类型或参数个数不同,才能构成方法的重载。  

2 当一个类中没有定义任何构造方法,Java将自动提供一个缺省构造方法;
3 子类通过super关键字调用父类的一个构造方法;
4 当子类的某个构造方法没有通过super关键字调用父类的构造方法,通过这个构造方法创建子类对象时,会自动先调用父类的缺省构造方法
5 构造方法不能被static、final、synchronized、abstract、native修饰,但可以被public、private、protected修饰;
6 构造方法不是类的成员方法;
7 构造方法不能被继承。

接口中方法默认是 abstract public,所以在接口只写函数声明是符合语法规则。但是变量默认是用public final static 修饰的,意思它是静态常量,常量不管在接口中还是类中必须在声明时初始化!

1.子父类存在同名成员时,子类中默认访问子类的成员,可通过super指定访问父类的成员,格式:super.xx (注:xx是成员名);
2.创建子类对象时,默认会调用父类的无参构造方法,可通过super指定调用父类其他构造方法,格式:s uper(yy) (注:yy是父类构造方法需要传递的参数)

类的初始化过程

也就是方法执行的过程 父类的静态变量->父类的静态代码块->子类的静态变量->子类的静态代码块->父类的非静态变量->父类的非静态代码块->父类的构造函数->子类的非静态变量->子类的非静态代码块->子类的构造函数 规律就是 父类先于子类 静态的先于非静态的 变量先于代码块

内联与final

《Java编程思想》中讲到final方法时提到,使用final方法原因有两个,
一、锁定方法。防止任何继承类修改、覆盖
二、效率。在java早期实现中,如果将一个方法指明为final,就是同意编译器将针对该方法的调用都转化为内嵌调用。…..
大概就是,如果是内嵌调用,虚拟机不再执行正常的方法调用(参数压栈,跳转到方法处执行,再调回,处理栈参数,处理返回值),而是直接将方法展开,以方法体重的实际代码替代原来的方法调用。这样减少了方法调用的开销。当然如果一个方法体本身就很大,这样的优势就小了很多了。
引用:函数/方法调用过程中CPU要进行现场处理(不妨看做是一种中断),当前的变量状态、寄存器状态、程序计数器PC。。。都要一一入栈保护起来,调用返回时又要一一出栈恢复以继续执行。相对于顺序执行流程,函数调用的入栈出栈带来了额外的开销,效率没有顺序执行高。
在最近的java设计中,虚拟机(特别是hotspot技术)可以自己去根据具体情况自动优化选择是否进行内联,因此和final关键字就无关了。
内联函数:
在计算机科学中,内联函数(有时称作在线函数或编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展(有时称作在线扩展);也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。
但在选择使用内联函数时,必须在程序占用空间和程序执行效率之间进行权衡,因为过多的比较复杂的函数进行内联扩展将带来很大的存储资源开支。另外还需要非常注意的是对递归函数的内联扩展可能带来部分编译器的无穷编译。
C/C++中可以声明内联函数,在java中不支持直接声明,但是jvm会根据情况进行优化内联。
内联函数特点:
(1)提升效率。如上说明。
(2)占更多内存空间。编译器直接将内联函数扩展开,调用多复制品就多,因此更占用内存。
(3)java中不需额外关注,jvm会自动进行优化
内联举例:
12 int max (int a, int b){ if (a > b) return a; else return b;}void main() { ..... a = max (x, y); // 内联,等价于 "a = (x > y ? x : y);" 直接扩展开了,不再调用方法 ..... }

值传递和引用传递

值传递:(形式参数类型是基本数据类型):
方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。
引用传递:(形式参数类型是引用数据类型参数):
也称为传地址。方法调用时,实际参数是对象(或数组),这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,这个结果在方法结束后被保留了下来,所以方法执行中形式参数的改变将会影响实际参数。

1、java语言参数之间只有值传递,包括按值调用和按引用调用。 一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。
按值调用:包括八大基本数据类型都是按值调用。传值的时候,也就是说方法得到的是所有参数值的一个拷贝。
按引用调用:数组、对象。传值时候,传递的是引用地址的拷贝,但是都是指向同一个对象。
2、String是不可变类(final and Immutable),这里只是把副本的指向修改成指向“test ok”,原地址str的指向的值没有发生改变。

多线程

以下哪几种方式可用来实现线程间通知和唤醒:(1&3 )

  1. Object.wait/notify/notifyAll
  2. ReentrantLock.wait/notify/notifyAll
  3. Condition.await/signal/signalAll
  4. Thread.wait/notify/notifyAll

wait()、notify()和notifyAll()是 Object类 中的方法
从这三个方法的文字描述可以知道以下几点信息:
1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;
有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。
上面已经提到,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,
等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。
Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,在阻塞队列那一篇博文中就讲述到了,阻塞队列实际上是使用了Condition来模拟线程间协作

线程结束的三个原因:

1、run方法执行完成,线程正常结束
2、线程抛出一个未捕获的Exception或者Error
3、直接调用该线程的Stop方法结束线程(不建议使用,容易导致死锁)

volatile与synchronized的区别:

volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
volatile仅能实现变量的修改可见性,但不具备原子特性,而synchronized则可以保证变量的修改可见性和原子性.
volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.
volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化.

main()函数即主函数,是一个前台线程,前台进程是程序中必须执行完成的,而后台线程则是java中所有前台结束后结束,不管有没有完成,后台线程主要用与内存分配等方面。

前台线程和后台线程的区别和联系:

  1. 后台线程不会阻止进程的终止。属于某个进程的所有前台线程都终止后,该进程就会被终止。所有剩余的后台线程都会停止且不会完成。
  2. 可以在任何时候将前台线程修改为后台线程,方式是设置Thread.IsBackground 属性。
  3. 不管是前台线程还是后台线程,如果线程内出现了异常,都会导致进程的终止。
  4. 托管线程池中的线程都是后台线程,使用new Thread方式创建的线程默认都是前台线程。
    说明:应用程序的主线程以及使用Thread构造的线程都默认为前台线程

用Thread建立的线程默认情况下是前台线程,在进程中,只要有一个前台线程未退出,进程就不会终止。主线程就是一个前台线程。而后台线程不管线程是否结束,只要所有的前台线程都退出(包括正常退出和异常退出)后,进程就会自动终止。一般后台线程用于处理时间较短的任务,如在一个Web服务器中可以利用后台线程来处理客户端发过来的请求信息。而前台线程一般用于处理需要长时间等待的任务,如在Web服务器中的监听客户端请求的程序,或是定时对某些系统资源进行扫描的程序
所以jre判断程序是否执行结束的标准是所有的前台线程运行完毕

继承

方法的重写(override)两同两小一大原则:

方法名相同,参数类型相同
子类返回类型小于等于父类方法返回类型,
子类抛出异常小于等于父类方法抛出异常,
子类访问权限大于等于父类方法访问权限。

子类的构造方法总是先调用父类的构造方法,如果子类的构造方法没有明显地指明使用父类的哪个构造方法,子类就调用父类不带参数的构造方法。
父类没有无参的构造函数,所以子类需要在自己的构造函数中显式调用父类的构造函数,
添加
super("nm");
否则报错:
Implicit super constructor Person() is undefined. Must explicitly invoke another constructor

class Person {
   String name = "No name";
   public Person(String nm) {
       name = nm;
   }     
}
class Employee extends Person {
   public Employee(String nm) {
       super(nm);
       // TODO Auto-generated constructor stub
   } 
   String empID = "0000";
}
public class Test {
   public static void main(String args[]) {
       Employee e = new Employee("123");
       System.out.println(e.empID);
   }
}

Java不支持多继承,但是通过一些巧妙的设计来达到和多继承同样的效果
通过接口、内隐类,继承、实现,互相配合,达到多继承的效果

内部类的声明和实例化

public class Enclosingone {
    //非静态内部类
    public class InsideOne {}
    //静态内部类
    public static class InsideTwo{}
}
 
class Mytest02{
    public static void main(String args []){
        Enclosingone.InsideOne obj1 = new Enclosingone().new InsideOne();//非静态内部类对象
        Enclosingone.InsideTwo obj2 = new Enclosingone.InsideTwo();//静态内部类对象
    }
}
  1. Java中一个类不能继承多个具体class。
  2. 一个类只可继承自一个具体 class,但可实现多个接口。interface不涉及到实现细节,不与任何存储空间有关连。可以实现合并多个 interface ,达到可向上转型为多种基类的目的。新类可继承自一个具象class,其余继承都得是interfaces。
  3. outer class不可继承自多个具体 class,可在其内部设多个inner class,每个inner class都能各自继承某一实现类。inner class不受限于outer class 是否已经继承自某一实现类。
  4. inner class可以说是多重继承问题的完整解决方案。inner class 可 “继承自多个具象或抽象类”。一个类不能继承自多个一般类。但我们可以让其内部的多个inner class各自继承某一实现类达到类似的目的。

this的使用时针对在方法内部使局部变量等值于实例变量而使用的一个关键字,此处的n是静态变量而非实例变量 所以this的调用会出错(试想一下,static本来是全类中可以使用的,是全局的,你非得this去调用,这不是区分局部变量和实例变量的分水线吗?但是此处是全局的,不需要区分)

多态

无论向上或向下转型,都记住一句话就可以了。
编译看左边,运行看右边。意思编译时候,看左边有没有该方法,运行的时候结果看 new 的对象是谁,就调用的谁。

接口里的方法只能用 public 和 abstract 修饰,如果你不写也没关系,默认的也是 public abstract 修饰.抽象方法不能有方法体

JVM

可简单分为三个区:

1、堆区(heap):用于存放所有对象,是线程共享的(注:数组也属于对象)

2、栈区(stack):用于存放基本数据类型的数据和对象的引用,是线程私有的(分为:虚拟机栈和本地方法栈)

3、方法区(method):用于存放类信息、常量、静态变量、编译后的字节码等,是线程共享的(也被称为非堆,即 None-Heap)

Java 的垃圾回收器(GC)主要针对堆区

JVM加载类的实现方式,我们称为 双亲委托模型
如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委托给自己的父加载器,每一层的类加载器都是如此,因此所有的类加载请求最终都应该传送到顶层的Bootstrap ClassLoader中,只有当父加载器反馈自己无法完成加载请求时,子加载器才会尝试自己加载。
双亲委托模型的重要用途是为了解决类载入过程中的安全性问题。
假设有一个开发者自己编写了一个名为Java.lang.Object的类,想借此欺骗JVM。现在他要使用自定义ClassLoader来加载自己编写的java.lang.Object类。然而幸运的是,双亲委托模型不会让他成功。因为JVM会优先在Bootstrap ClassLoader的路径下找到java.lang.Object类,并载入它

大多数 JVM 将内存区域划分为 Method Area(Non-Heap)(方法区 ), Heap(堆 ), Program Counter Register(程序计数器 ), VM Stack(虚拟机栈,也有翻译成JAVA 方法栈的), Native Method Stack ( 本地方法栈 ),其中Method Area 和 Heap 是线程共享的 ,VM Stack,Native Method Stack 和Program Counter Register 是非线程共享的。为什么分为 线程共享和非线程共享的呢? 请继续往下看。

首先我们熟悉一下一个一般性的 Java 程序的工作过程。一个 Java 源程序文件,会被编译为字节码文件(以 class 为扩展名),每个java程序都需要运行在自己的JVM上,然后告知 JVM 程序的运行入口,再被 JVM 通过字节码解释器加载运行。那么程序开始运行后,都是如何涉及到各内存区域的呢?

概括地说来,JVM初始运行的时候都会分配好 Method Area(方法区)Heap(堆) ,而JVM 每遇到一个线程,就为其分配一个 Program Counter Register(程序计数器) , VM Stack(虚拟机栈)Native Method Stack (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放掉。这也是为什么我把内存区域分为线程共享和非线程共享的原因,非线程共享的那三个区域的生命周期与所属线程相同,而线程共享的区域与JAVA程序运行的生命周期相同,所以这也是系统垃圾回收的场所只发生在线程共享的区域(实际上对大部分虚拟机来说知发生在Heap上)的原因。

网络

Socket套接字
就是源Ip地址,目标IP地址,源端口号和目标端口号的组合
服务器端:ServerSocket提供的实例
ServerSocket server= new ServerSocket(端口号)
客户端:Socket提供的实例
Socket soc=new Socket(ip地址,端口号)

异常

异常类图
都是Throwable的子类:

  1. Exception(异常): 是程序本身可以处理的异常。
  2. Error(错误): 是程序无法处理的错误。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,一般不需要程序处理。
  3. 检查异常(编译器要求必须处置的异常) : 除了Error,RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。
  4. 非检查异常(编译器不要求处置的异常): 包括运行时异常(RuntimeException与其子类)和错误(Error)。

Java语言中的异常处理包括声明异常、抛出异常、捕获异常和处理异常四个环节。
throw用于抛出异常。
throws关键字可以在方法上声明该方法要抛出的异常,然后在方法内部通过throw抛出异常对象。
try是用于检测被包住的语句块是否出现异常,如果有异常,则抛出异常,并执行catch语句。
cacth用于捕获从try中抛出的异常并作出处理。
finally语句块是不管有没有出现异常都要执行的内容。

finally 语句块是在 try 或者 catch 中的 return 语句之前执行的。

因为函数调用是入栈出栈,栈是在寄存器之下的速度最快,且占的空间少,而自定义异常是存在堆中,肯定异常的内存开销大!

数据结构

Collection
——List
————LinkedList 非同步
————ArrayList 非同步,实现了可变大小的元素数组
————Vector 同步
——Set 不允许有相同的元素
Map
——HashTable 同步,实现一个key--value映射的哈希表,key和value都不允许出现null值
——HashMap 非同步,
——WeakHashMap 改进的HashMap,实现了“弱引用”,如果一个key不被引用,则被GC回收
注:
List接口中的对象按一定顺序排列,允许重复
Set接口中的对象没有顺序,但是不允许重复
Map接口中的对象是key、value的映射关系,key不允许重复

常见的哈希冲突解决方法:

  1. 开放地址法
  2. 链地址法(拉链法)
  3. 再散列
  4. 建立一个公共溢出区

都是解决哈希填冲突的策略,但是在java.util.HashMap中,总体来说是使用的链地址法来解决冲突的。

解决哈希冲突常用的两种方法是:开放定址法和链地址法
开放定址法:当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。沿此序列逐个单元地查找,直到找到给定 的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探查到开放的 地址则表明表中无待查的关键字,即查找失败。
链地址法:将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数 组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。

文件

./表示当前项目的路径
../表示当前目录的父目录路径
盘符:\name\file 或者 盘符:/name/file
表示物理路径
"\"这个符号在中英文环境下是不一样的显示;而"/"在中英文环境下是相同的显示。所以前者需要转义。

Struts

Struts与MVC对应关系
Structs与MVC对应关系

数据库

Statement是sql语句的载体
Statement是标准的Statement类,通过字符串对sql语句进行拼接,但是它存在sql注入的危险
PreparedStatement对sql语句进行了预编译,可以防止SQL注入
CallableStatement用来调用存储过程的
BatchedStatement用于批量操作数据库,BatchedStatement不是标准的Statement类

为了保证数据库一致性,在多事务访问数据库时,数据库为事务提供锁机制,共享锁,既该锁为共享状态,其他事务可以读取数据库,但不能修改,排他锁,即排除其他事务,在没有释放锁资源,其他事务不能对数据库访问和修改

drop table Student 删除表中的数据以及定义
truncate table Student 删除表中的数据,但是表的定义还在
delete table Student 系统一行一行删除数据,保留日志,可以回滚

并发操作可能破坏事务的隔离性,带来的数据不一致性包括三类:丢失修改、不可重复读、读“脏”数据

数据库中的三种完整性
域完整性:

    域完整性是对数据表中字段属性的约束,通常指数据的有效性,它包括字段的值域、字段的类型及字段的有效规则等约束,它是由确定关系结构时所定义的字段的属性决定的。限制数据类型,缺省值,规则,约束,是否可以为空,域完整性可以确保不会输入无效的值.

实体完整性:

    实体完整性是对关系中的记录唯一性,也就是主键的约束。准确地说,实体完整性是指关系中的主属性值不能为Null且不能有相同值。定义表中的所有行能唯一的标识,一般用主键,唯一索引 unique关键字,及identity属性比如说我们的身份证号码,可以唯一标识一个人。

参照完整性:

    参照完整性是对关系数据库中建立关联关系的数据表间数据参照引用的约束,也就是对外键的约束。准确地说,参照完整性是指关系中的外键必须是另一个关系的主键有效值,或者是NULL。参考完整性维护表间数据的有效性,完整性,通常通过建立外部键联系另一表的主键实现,还可以用触发器来维护参考完整性

NoSQL数据库的四大分类

键值(Key-Value)存储数据库

这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。Key/value模型对于IT系统来说的优势在于简单、易部署。但是如果DBA只对部分值进行查询或更新的时候,Key/value就显得效率低下了。[3] 举例如:Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB.,Google BigTable ,Amazon DynamoDB

列存储数据库

这部分数据库通常是用来应对分布式存储的海量数据。键仍然存在,但是它们的特点是指向了多个列。这些列是由列家族来安排的。如:Cassandra, HBase, Riak.

文档型数据库

文档型数据库的灵感是来自于Lotus Notes办公软件的,而且它同第一种键值存储相类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON。文档型数据库可 以看作是键值数据库的升级版,允许之间嵌套键值。而且文档型数据库比键值数据库的查询效率更高。如:CouchDB, MongoDb. 国内也有文档型数据库SequoiaDB,已经开源。

图形(Graph)数据库

图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。NoSQL数据库没有标准的查询语言(SQL),因此进行数据库查询需要制定数据模型。许多NoSQL数据库都有REST式的数据接口或者查询API。[2] 如:Neo4J, InfoGrid, Infinite Graph.

因此,我们总结NoSQL数据库在以下的这几种情况下比较适用:1、数据模型比较简单;2、需要灵活性更强的IT系统;3、对数据库性能要求较高;4、不需要高度的数据一致性;5、对于给定key,比较容易映射复杂值的环境

评论
  1. 回复
    1. @悲伤的橘子树

      见笑了见笑了

      回复
  2. Java8 的para.4 Lambda写成Landbda了哦~改一下
    话说整理的真的超详细!看完有种想写代码的冲动哈哈哈 太久没碰这个已经有种陌生的感觉了(写了一学期汇编)~

    回复
    1. @Gazzz

      哈哈,谢谢提醒,已修正,看的挺仔细啊。
      见笑了,整理比较零散,只是大致分了下类别,有时间再好好梳理一下
      搞点小事情也是不错的୧(๑•̀⌄•́๑)૭
      还有,写汇编的都是大佬呀

      回复
      1. @一去二三遥

        我们Java只是选修课,学的都是些很皮毛的东西,看到文章构造块那里懂得人可能觉得这是个好题,我看的话就是查概念查概念
        汇编的话是课程要求(老师说C语言你们都写这么多了不如就练练汇编好了,然后整学期的作业全部要求汇编写
        现在有冲动去系统的学学java了呢哈哈哈

        回复
        1. @Gazzz

          写汇编的每天都在和CPU和内存打交道所以看这种题很简单的
          Java程序通过java虚拟机运行,跟底层至少还隔着JVM
          基础要深的点考就考这种类加载或者构造器,一般没有踩过坑或者注意过的很难理解
          我做这题也是一脸懵逼

          回复