java知识点总结

1、JDK和JRE的区别

JDK是java的开发工具集,包含编译工具(javac)和打包工具(jar)等。他还包含jre。(所以安装jdk就可以了,不需要单独装jre)。

JRE是java运行环境,他包含java的核心类库和jvm虚拟机。

2、java程序运行机制

编写好源文件(.java文件)以后,通过jdk中的编译指令(javac)进行编译,生成字节码文件(.class文件),然后通过jvm虚拟机进行加载,执行,最后运行出结果

3、垃圾回收机制

在java中,当一个对象不再被引用时,就会被垃圾回收机制(GC)处理掉。垃圾回收机制的工作内容就是回收无用对象的所占的内存空间,这些内存空间都是jvm堆内存的内存空间,垃圾回收只能回收内存资源,对物理资源无法回收(如数据库连接、io流等资源)。导致java内存溢出的原因就是无用对象没有被GC机制处理掉,而一直占用着内存空间。java的栈在某些情况下也会内存溢出,例如出现方法递归,有两个方法不停地相互调用,一直进栈不出栈,最终会导致栈内存溢出。

堆内存和栈内存的区别:当一个方法执行时,会进栈(任何方法执行都会进栈),并在栈中开辟自己的空间(在涉及到线程问题时,一条线程对应一个栈区)。当方法执行结束后,对应的内存空间也会随之释放(弹栈)。在程序中创建一个对象时(在某个方法中创建对象),该对象会保存在堆内存中,这个对象中的方法会被反复调用(反复进栈出栈),当这个对象失去引用时,就会被垃圾回收机制处理掉。在声明并创建一个对象的时候,引用保存在栈内存,对象则保存在堆内存。如果这个类存在静态变量,静态变量保存在方法区,方法区分为静态区(静态存储区)和动态区。静态区一般存放静态变量、静态方法以及主函数;动态区存放类的普通方法。此外,还有一个常量池的概念(常量池也是方法区的一部分)。栈内存中保存的是对象引用、基本数据类型变量的引用和值。而常量池中保存的则是基本数据类型常量和字符串值(因为字符串值也是常量值,String被final修饰)。在声明一个字符串引用并创建字符串对象时,如“String a=new String(“qwe”) ”,有可能创建一个对象或者两个对象。他会先去常量池中寻找对应的字面值“qwe”,如果不存在,就会在常量池中创建这个字面值并在对内存中再创建一个对应这个字面值的拷贝对象。如果存在,则会直接在堆内存中创建一个对应这个字面值的拷贝对象。

在垃圾回收机制回收任何对象之前,总会先调用该对象的finalize()方法。该方法可能回使该对象复活(重新被引用),从而导致垃圾回收机制取消回收。当一个对象在堆内存中运行时,根据它被引用变量所引用的状态可以把它所处的状态分为以下三种:

(1)可达状态:当一个对象被创建以后,如果有一个及一个以上的引用变量引用它的时候,这个对象就处于可达状态,程序可以通过引用变量来调用该对象的方法和属性。

(2)可恢复状态:当一个对象在程序中不再有任何引用变量引用它的时候,即为可恢复状态。这种状态下,系统的垃圾回收机制就会准备回收该对象所占用的内存,在回收之前,会调用该对象的finalize()方法,这个方法执行后,可能会让该对象重新被引用变量所引用,这时便回到了可达状态,如果没有被引用,则会进入不可达状态。

(3)不可达状态:当一个对象切断了所有和引用变量之间的引用,并且系统已经调用了finalize()方法之后,该对象仍然没有变成可达状态,那么这个对象将永久的失去引用,成为了不可达状态。这时,系统的垃圾回收机制才会对该对象所占用的内存进行回收。

java知识点总结_第1张图片

由于系统的垃圾回收机制存在时间、执行上的不确定性,java还提供了强制垃圾回收,这种强制只是通知系统进行垃圾回收。但是系统是否进行垃圾回收仍然不确定。但大部分时候,强制回收都会有一些效果。强制垃圾回收存在以下两种方式:System.gc();Runtime.getRuntime().gc();

关于对象的引用方式分为4种:(1)强引用:该引用是最常见的一种引用方式,程序中创建一个对象并把该对象直接赋给一个引用变量。程序通过该引用变量来调用该对象的方法和属性,当一个对象被一个或一个以上引用变量引用时,该对象就处于可达状态,不会被垃圾回收机制回收。(2)软引用:软引用通过SoftReference类来实现,当一个对象只有软引用时,有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存充足时,他不会被回收,当内存不足时,可能被垃圾回收机制回收。软引用通常被应用到内存敏感的程序中。(3)弱引用:弱引用通过WeakReference类实现,他和软引用很像,但是引用级别更低,当垃圾回收机制运行时,不论内存是否充足,弱引用所引用的对象都会被处理掉。(4)虚引用:虚引用通过PhantomReference类来实现,虚引用类似于对象没有引用,如果对象只有一个虚引用,那么他和没有引用的状态几乎相同。虚引用主要用于跟踪对象被垃圾回收的状态。

4、基本数据类型

byte(字节型)、char(字符型)、boolean(布尔型)、short(短整型)、int(整型)、long(长整型)、float(单精度浮点型)、double(双精度浮点型)

小的类型转大的类型,自动转换,大转小,需要做强转。

基本数据类型和包装类的之间的相互转换:如基本数据类型 int a=2; 转成int的包装类Integer Integer b=new Integer(a); 包装类转换成基本数据类型 int c=b.intValue();其他类型也是如此。在java的1.5以后出现了新特性:可以自动拆装箱。即 int a=2; Integer b=a;

java中的数据类型分为基本数据类型和引用类型,基本数据类型就是上面列出的那些,引用类型一般是自己创建的java类,但是String也属于引用类型。

5、数组

int[] a;   声明数组   a=new int[5]  动态初始化数组  a={1,2,3,4} 静态初始化数组

6、面向对象

面向对象三大特征:封装、继承、多态。面向过程:强调的是功能行为,先干啥,再干啥。面向对象:强调的是具备某种功能的对象。关于多态就是:父类变量可以直接指向子类的实例化对象。反过来子类变量也可以直接指向父类的实例化对象(需要强转)。

7、类

类是对一种事物的共同点的一种抽象,将一组事物的公共点抽象出来。对象则是具体的,具备这些公共点的具体事务。关于类中的构造方法,多个构造函数说简单点就是方法重载。

8、static关键字

static用于修饰类中的方法和属性,被它修饰的方法可以通过类名来调用。它随着类的加载而加载,优先于对象存在。

9、代码块

代码块就是指使用“{}”括起来的一段代码,代码块可以分为4种:

1、普通代码块

java类中直接定义的方法。如 

public void test(){
System.out.print(123);
}

 

2、构造代码块

 

直接定义在java类中的代码块。如 

{
System.out.print(456);
} //构造代码块优先于构造函数执行,每次实例化对象之前都会先执行构造代码块。

 

3、静态代码块

 

使用static关键字来修饰的代码块。如 

static {
System.out.print(789);
}  //优先于主方法执行,优先于构造代码块执行,只执行一次,可以用于给静态变量赋值。

 

4、同步代码块

synchronized(obj){
    //同步代码块内容
}

在使用多线程编程的时候,会出现并发问题,并导致数据出现错误,这时需要将方法中可能会导致线程问题的代码放入同步代码块中,并传递相应参数。这样,当程序运行到这个代码块的时候,会通过传递的参数来锁定执行的线程。以此来避免数据错误的问题。

10、Singleton模式(单例模式) 饿汉式和懒汉式

目的:在整个应用中有且只能有一个实例,所有指向该类型的引用都指向该实例。

饿汉式实例:直接将实例定义出来

class SingleDemo{
    private static SingleDemo s1=new SingleDemo();
    private SingleDemo(){
        //将构造函数私有化,这样外部就不能实例化该类了
    }
    public static SingleDemo getSingle(){
        return s1;
    }
}

 

懒汉式实例:只给变量,不给实例初始化

 

class SingleDemo{
    private static SingleDemo s1;
    private SingleDemo(){
        //将构造函数私有化,这样外部就不能实例化该类了
    }
    public static SingleDemo getSingle() 
   {
        if(s1==null){
            s1=new SingleDemo();
        }
        return s1;
    }
}

 

这两种方式都使用static修饰,但饿汉式直接将实例定义出来,因为static的缘故,每次都会随着类的加载而加载,会损耗性能,但相对简单。懒汉式只会第一次加载较慢,但线程不安全。

 

11、抽象类

通过abstract关键字来修饰的类叫做抽象类,抽象类不能被实例化,抽象类中可以包含抽象方法和普通方法,抽象方法也是通过abstract来修饰,并且抽象方法只能做声明,不能有内容。如果一个类继承了一个抽象类,就必须实现他的所有抽象方法。

12、抽象类的体现-模板模式

抽象类是多个具体子类抽象出来的父类,以该抽象类作为子类的模板可以避免子类设计的随意性。抽象类的体现主要就是模板设计模式。抽象父类只定义用到的某些方法,具体交给子类去实现。抽象方法不能定义void返回类型。

13、接口

接口相当于更加抽象的抽象类,接口中全是抽象方法,接口之间可以实现多继承。一个类可以实现多个接口。接口不能被实例化,但是可以声明接口并指向他的实现类。

14、面向接口编程之简单工厂模式

简单来说就是构建一个工厂出来,在里面进行创建,用的时候直接传递参数来用。这样做的好处:可以屏蔽不同子类实现的差异,提高代码的可拓展性和可维护性。

interface Phone{//制定标准,要实现的接口
    public void send();
}
//实现接口
class IPhone implements Phone{
    public void send(){
        System.out.print("IPhone在发送短信");
    }
}
class AndroidPhone implements Phone{
    public void send(){
        System.out.print("AndroidPhone在发送短信");
    }
}
class MyPhone implements Phone{
    public void send(){
        System.out.print("我的手机在发送短信");
    }
}
//建立工厂
class Factory{
    public static void show(String s){
        if(s.equals("")){
            System.out.print("您的输入为空");
            return;
        }
        //声明接口
        Phone p=null;
        if(s.equals("IPhone")){
            p=new IPhone();
        }else if(s.equals("AndroidPhone")){
            p=new AndroidPhone();
        }else if(s.equals("MyPhone")){
            p=new MyPhone();
        }
        p.send();
    }
}
//调用该工厂
class Test{
    public static void main(String[] args){
        new Factory().show("");
        new Factory().show("IPhone");
        new Factory().show("AndroidPhone");
        new Factory().show("MyPhone");
    }
}
//运行结果:您的输入为空、IPhone在发送短信、AndroidPhone在发送短信、我的手机在发送短信

15、面向接口编程之适配器模式

有一个现成的类,但是他实现的接口的方法只有一个方法是你需要的,别的都不需要,这是就需要使用适配器模式。

//定义接口,包含如下方法:open()、close()、loading(),最终只想用loading方法
interface Window{
    void open();
    void loading();
    void close();
}
//定义一个类,实现上述接口
class AdapterWindow implements Window{
    public void open(){
        System.out.print("打开");
    }
    public void loading(){
        System.out.print("加载中");
    }
    public void close(){
        System.out.print("关闭");
    }
}
//在定义一个类,继承上边的类,复写loading方法
class MyWindow extends AdapterWindow{
    //覆写父类的方法
    public void loading(){
        System.out.print("正在加载中");
    }
}
//调用测试
class Test{
    public static void main(String[] args){
        new MyWindow().loading();
    }
}
//显示结果:正在加载中

16、内部类

在一些情况下,一个类会被定义到另一个类的内部,这个定义在其他类内部的类叫做内部类(嵌套类)。包含内部类的类被称作外部类。内部类相当于外部类的成员,所以内部类可以访问其外部类的其他成员。但是外部类不能访问其内部类的实现细节,比如内部类的成员变量。匿名内部类适用于创建那些只需要使用一次的类。内部类的定义和正常类的定义基本相同,只是多了三个修饰符:private、protected、static。

大部分时候内部类都会被作为成员内部类。而局部内部类和匿名内部类都不属于类成员。

17、匿名内部类

匿名内部类适用于那些只需要使用一次的类。他不能重复使用。匿名内部类的应用:将方法参数定义为接口时,正常在调用这个方法的时候,需要新建一个类实现接口(新定义一个java文件),比较麻烦。这时就可以以匿名内部类的形式实现接口作为入参。

new 父类构造器(参数列表)|实现接口()  
    {  
     //匿名内部类的类体部分  
    }

 

18、枚举类

枚举类通过enum关键字来定义,创建的枚举类默认继承java.lang.enum类而不是object类。该类实现了java.lang.Serializable接口和java.lang.Comparable两个接口。使用enum创建的非抽象的枚举类默认被final修饰,所以枚举类不能衍生子类。枚举类的构造器只能使用private。并且枚举类的实例必须在第一行显示列出,否则永远不能创建枚举类。

 

非线程安全是指多线程操作同一个对象可能会出现问题。而线程安全则是多线程操作同一个对象不会有问题。

 

19、String类、StringBuffer类和StringBuilder类

字符串就是一连串的字符序列,String类是不可变类,该对象一旦被创建以后就不能改变,直到销毁。StringBuffer则是一个字符序列可变的字符串,有相关的一些系列方法,同时可以通过toString()方法将StringBuffer对象转换成String对象。JDK1.5以后增加了StringBuilder类,该类和StringBuffer类功能上几乎相同。不同的是StringBuffer是线程安全的,StringBuilder是线程不安全的,但StringBuilder的性能相对较高。

20、java集合类

java的集合类主要由两个接口衍生,分别是Collection和Map。这两个接口包含了一些子接口和实现类。其中,Set和List是Collection派生出来的子接口,分别代表了无序集合和有序集合。(Queue也是Collection下派生出来的,它是队列)

List和Set的异同点:List元素可重复、元素有序(和添加顺序一致)、元素可为空。Set元素不可重复、元素无序、元素可为空

List子类:ArrayList(常用)、LinkedList、Vector

ArrayList底层数据结构是顺序存储(便于查询),LinkedList底层数据结构是链表(便于存储)(同时LinkedList也可以还实现了‘双端队列’,即可以当作队列来用,也可以当作来使用)。Vecotor和ArrayList差不多,但是线程安全,这也就意味着它速率比ArrayList慢。

Set子类:HashSet(常用)、TreeSet、EnumSet

HashSet底层数据结构是哈希表结构,便于查找和删除,添加慢。向HashSet中存储自定义对象需要重写hashCode()和equals()方法。TreeSet底层数据结构是二叉树,数据越多越慢。EnumSet是专门为枚举类型存在的集合类。

Queue队列子类:PriorityQueue、ArrayDeque

PriorityQueue没有按照队列‘先进先出’的标准来读取,而是按照大小来读取,每次取得数据都是队列中最小的元素。并且PeiorityQueue不允许存储空值。

ArrayDeque则是一个双端队列,它既可以当作‘先进先出’的队列来使用,也可以当作‘后进先出’的栈来使用。

Map子类:HashMap(常用)、HashTable、LinkedHashMap、Properties、TreeMap、WeakHashMap、EnumMap

HashMap效率高,线程不安全。由于他的key不能重复,所以只能有一个key为null,但是value可以随意null。

HashTable效率相对差一些,线程安全。他的key和value都不允许为null。

LinkedHashMap作为HashMap的子类,底层则使用了双向链表。使用它存储遍历数据时,她会按照存储时的顺序来进行读取。它的性能略低于HashMap。

Properties则是HashTable的子类,通过它可以很方便的读写属性文件。它包含读写文件的相关方法,除此之外,他相当于一个key、value都是String的Map。

TreeMap向名字说的那样,底层实现就是二叉树。

WeakHashMap中的key是弱引用,当执行垃圾回收机制时,map中的键值对会被清除。而HashMap中的key是强引用,除非HashMap失去引用,否则永远不会被垃圾回收机制处理。

21、异常处理

java异常分为Error和RuntimeException两种类型。在写catch块的时候如果可能涉及到多个异常,要先将小的异常放在前边,大的异常放在后边。否则会引起编译错误。因为大的异常已经包含小的异常。除非在try、catch块中调用退出虚拟机的方法System.exit(1);finally块中的代码不会执行。否则无论怎样都会被执行。

在java7以后,出现了增强try语句的功能,在try后边紧跟一对儿小括号,并且在小括号里面声明并初始化一些资源类的对象。(这里的资源指的是数据库连接、IO流之类的)这些对象可以不用通过finally块自动释放资源。但是这些类都必须实现AutoCloseable或者Closeable接口。自动关闭资源的try语句相当于包含隐式的finally块。

在catch块捕获到一个异常以后,接着抛出另一个异常,并把原始异常欣喜保存起来是一种典型的链式处理(也称作职责链模式)。

22、数据库

数据库的索引相当于一本书的目录,当给数据库表格的某个字段添加索引以后,可以提高查询速度。添加索引的语句格式如下:create index index_name on table_name(列名...);其中列名可以是多个。

数据库事务:数据库事务是由一步或几步组成的逻辑执行单元,这一系列操作要么全部执行,要么全部不执行。事务具备以下四个特性:原子性、一致性、隔离性、持续性。

数据库连接池:当程序启动时,系统会主动创建多个数据库连接,并通过这些数据库连接组成一个连接池。每次程序请求数据库连接时,不会新建数据库连接,而是去连接池中取出已有的连接。使用完以后,不会关闭该连接,而是将该数据库连接返还给连接池。通过使用连接池可以大大提高效率。

23、java注解

java的注解分为系统提供的注解和自定义的注解。注解的主要作用有以下几点:1、提供信息给编辑器(编辑器可以利用注解来探测错误和警告信息)。2、编译阶段时的处理(软件工具可以利用注解信息来生成代码、html文档或是做其他处理)。3、*运行时的处理(一些注解可以接受运行时代码的提取)。最常用的就是这个功能,可以通过反射来提取注解的相关信息。

系统提供的注解有以下几种:@Deprecated   该注解用于标识已过时的元素。@Override 该注解标识正在复写父类的方法(通常会在接口的实现类的方法上看到)。@SuppressWarnings  该注解会取消已过时的元素的警告  @SafeVarargs 参数安全类型的注解  @FunctionalInterface  函数式接口注解 

自定义注解:注解通过  @interface 关键字进行定义。

public @interface testAnnotation{
}

@testAnnotation
public class Test{
}
//上边的代码自定义并应用了一个注解

注解的定义和接口十分相似,只是在interface前边加上一个@。不过,这样定义的注解并没有什么意义,想要自定义的注解有实用性,需要在自定义注解上添加元注解元注解就是可以注解到注解上的注解。元注解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

@Retention

Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。

它的取值如下: 
- RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。 
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。 
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们(常用)。只有存活到程序运行时的注解才能通过反射来读取到注解信息。

@Documented

顾名思义。它的作用是能够将注解中的元素包含到 Javadoc 中去。

@Target

Target 是目标的意思,@Target 指定了注解运用的地方。@Target 有下面的取值:

  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
  • ElementType.CONSTRUCTOR 可以给构造方法进行注解
  • ElementType.FIELD 可以给属性进行注解
  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
  • ElementType.METHOD 可以给方法进行注解
  • ElementType.PACKAGE 可以给一个包进行注解
  • ElementType.PARAMETER 可以给一个方法内的参数进行注解
  • ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

@Inherited

Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。 
说的比较抽象。代码来解释。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}


@Test
public class A {}


public class B extends A {}

注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。

@Repeatable

Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。

什么样的注解会多次应用呢?通常是注解的值可以同时取多个。

举个例子,一个人他既是程序员又是产品经理,同时他还是个画家。


@interface Persons {
    Person[]  value();
}


@Repeatable(Persons.class)
@interface Person{
    String role default "";
}


@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{

}

注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。

什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解。

注解属性:注解的属性也称作成员变量,注解只包含成员变量,没有方法。注解在定义成员变量时,形式很特别。他采用的是“无形参的方法”来进行定义的。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {

    int id();

    String msg();

}

上面代码定义了 TestAnnotation 这个注解中拥有 id 和 msg 两个属性。在使用的时候,我们应该给它们进行赋值。

赋值的方式是在注解的括号内以 value=”” 形式,多个属性之前用 ,隔开。

@TestAnnotation(id=3,msg="hello annotation")
public class Test {

}

需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。

注解中属性可以有默认值,默认值需要用 default 关键值指定。比如:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {

    public int id() default -1;

    public String msg() default "Hi";

}

TestAnnotation 中 id 属性默认值为 -1,msg 属性默认值为 Hi。 
它可以这样应用。

@TestAnnotation()
public class Test {}

因为有默认值,所以无需要再在 @TestAnnotation 后面的括号里面进行赋值了,这一步可以省略。

另外,还有一种情况。如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内。

public @interface Check {
    String value();
}

上面代码中,Check 这个注解只有 value 这个属性。所以可以这样应用。

@Check("hi")
int a;

这和下面的效果是一样的

@Check(value="hi")
int a;

最后,还需要注意的一种情况是一个注解没有任何属性。比如

public @interface Perform {}

那么在应用这个注解的时候,括号都可以省略。

@Perform
public void testMethod(){}

24、mybatis一级缓存与二级缓存

 缓存存在的意义就是为了提高程序性能,减少数据库压力。

mybatis的一级缓存默认为开启状态,一级缓存是sqlSession级别的缓存,在使用mybatis作为持久层框架时,当需要操作数据库时,需要构建sqlSession对象,在这个对象中有一个数据结构(HashMap)用户存储缓存数据。不同的sqlSession之间的数据不共享。当需要对数据库进行增删改操作时,需要清空缓存。这样可以避免脏读。一级缓存就是同一个sqlSession对象下,如果多次调用同一个查询语句,并且传递的参数一致,这样就只有第一次查询需要走sql语句,其余的查询都会走缓存。二级缓存则是mapper级别的缓存,mybatis的二级缓存需要手动在mapper中配置,二级缓存的作用范围相对于一级缓存更大,多个sqlSession对象之间可以共享缓存数据。

25、IO流

java通过输入、输出流来控制文件,他分为两种,分别是字节流和字符流。除了操作的数据单位不同,用法几乎都一样。java所有的IO流都是以这两种流为基础的。RandomAccessFile类可以用来给文件追加内容,或者在文件中间插入内容。IO子类根据具体需求去查文档即可。

26、对象序列化

java对象序列化的目的就是将java对象保存到磁盘中或者在网络中直接传输对象。对象序列化机制把java对象转换成和平台无关的二进制流。对象的序列化简单说就是将java对象存入IO流中,与之对应的则是java的反序列化,他是通过IO流将java对象恢复。如果想让某个java对象支持序列化机制,就需要实现Serializable或者Externalizable这两个接口之一。

27、NIO

NIO意为new IO 就是新的IO流。新的IO流采用了不同的方式来处理文件的输入和输出。新IO主要就是使用内存映射文件的方式。就是将文件或者文件的部分内容映射到内存中。这样就可以像访问内存一样访问文件内容了。Channel(通道)和Buffer(缓冲)是NIO的两个核心对象。Channel和传统IO最大的区别就是他有一个map()方法。他可以将一块数据直接映射到内存中。传统的IO是面向流的处理,NIO则是面向块的处理。

从文件转换到二进制的过程叫做编码,从二进制到文件的过程叫做解码。(字符到字节:编码。字节到字符:解码)

28、多线程

线程和进程:操作系统可以执行多个任务,每个任务就是进程。进程可以执行多个任务,每个任务就是线程(在多个线程执行时,系统会为每个线程分配对应的栈区,某一个线程挂掉的时候不会影响其他线程)。

并发性:在同一时刻只能有一条指令执行,对于cpu而言,在某一个时间点只能执行一个程序,因为cpu的运行速度特别快,才会给人觉得多个程序在同时执行。

同步函数、同步代码块以及静态同步函数区别:同步函数的锁默认是该对象(this),静态同步函数的锁则是该类的字节码文件对象(*.class)。同步代码块可以指定任意对象,在对代码做同步操作时,只需要将可能出现线程问题的代码做同步操作即可。

多线程的创建(1):继承Thread类,实现run()方法。run()方法就是线程的执行体。线程启动:new 子类().start() *使用继承Thread类来创建的线程类,多个线程无法共享线程类的实例变量。

多线程的创建(2):实现Runnable接口,实现run()方法。run()方法就是线程的执行体。线程启动:new Thread(Runnable实现类).start()*实现Runnable接口来创建的线程类,多个线程共享线程类的实例变量。

多线程的创建(3):实现Callable接口,实现call()方法。该方法返回一个泛型结果。将该实现类作为参数实例化一个FutureTask类。FutureTask实现了Runnable接口。*通过这种方式创建的线程,多个线程共享线程类的实例变量。一般使用下边两种方式实现多线程。

线程的执行状态大概可以分为以下几种:新建(调用start())、运行(调用start方法后可能会来到该状态,也可能来到临时阻塞状态)、临时阻塞(调用start方法后,因为当前时间cpu正在执行别的线程,当前线程拥有执行资格,但是没轮到他)、冻结(对于运行状态的线程或者临时阻塞状态的线程可以通过sleep方法设置睡眠时间暂时释放被cpu执行的资格,也可以通过wait方法释放被cpu执行的资格,这个需要notify方法来唤醒,唤醒以后的线程再次回到运行或者临时阻塞状态)、消亡(线程中的内容执行结束以后,该线程来到消亡状态释放内存)。对于已经死亡或者已经调用过start()方法的线程,不能再次调用start()方法,否则会引发线程异常。【唤醒、等待等方法在操作时必须都是同一个对象(同一个锁)】Thread类下提供了一系列控制线程的方法,join()方法。在程序执行流程中,当一个线程调用join()方法后,会立即阻塞当前执行的线程,并运行调用join的线程,直到调用join的线程执行结束。setDaemon(true)方法。当一个线程调用该方法后,就会变成守护线程(也称作后台线程)。他的任务就是为其他线程提供服务,当程序中所有前台线程都死亡以后,后台线程才会死亡,否则会一直存活。(jvm的垃圾回收线程就属于后台线程)类方法sleep()方法。调用sleep()方法可以让当前执行的线程由运行状态变为阻塞状态,该方法可以设置阻塞时间。类方法yield()方法。调用yield()方法可以让当前执行的线程由运行状态变为就绪状态。调用该方法可能会使线程指停止一瞬间又被线程调度器重新调用。(该方法也可以让线程停止运行。但通常使用sleep()方法)线程存在优先级的概念,优先级高的线程会获得较多的执行机会。线程可以手动设置优先级。

在使用多线程编程的时候,由于多线程的并发性,可能会导致多个线程并发操作同一个数据的时候出现数据错误。这时就需要使用到同步代码块或者同步方法。同步代码块和同步方法可以阻止多个线程对他们的同一个共享资源做并发访问。只需在可能出现线程安全问题的方法上加synchronized修饰即可。同步代码块和同步方法都属于隐式的控制线程同步。java5以后出现了显式的线程同步控制。就是Lock。其中最常用的就是ReentrantLock类。在类中声明并定义实例化一个ReentrantLock的常量。在有线程问题的方法里面调用lock()方法加锁。在方法结束的时候调用unlock()方法解锁。这样就能显式的控制线程问题了。

线程通信:在多线程的程序中,由于线程调度器的随机性,无法准确控制线程的轮换执行。java也提供了一些机制来控制线程的协调运行。针对隐式的线程控制,提供了wait()【让线程等待】、notify()【让线程继续执行】、notifyAll()【让线程全部执行】等Object类的方法来控制。其中如果使用的是同步代码块,这些方法则由代码块传递的参数来调用。如果是同步方法,则直接调用。针对显式的线程控制Lock,则提供了Condition类。该类的对象由Lock的newCondition()方法获取。该类也提供了三个类似的方法,分别是await()signal()signalAll()

线程组:简单来说就是一个装载线程的数组。通过它可以对多个线程进行统一控制。

线程池:与数据库连接池的概念类似。当系统多次创建线程的时候是非常消耗性能的,这时就需要用到线程池。线程池在系统启动时会创建一定数量的空闲线程。通过线程池可以有效的控制系统中线程并发的数量。程序将Runnable对象或者Callable对象传给线程池,线程池就会启动一个线程来执行他的run()或者call()方法。当执行结束后,该线程不会死亡,而是重新来到空闲状态。等待执行下一个Runnable对象或者Callable对象。

29、反射

每个类被jvm虚拟机加载之后,都会生成一个Class对象。通过这个Class对象就可以访问jvm中的这个类。获取这个Class对象通常有三种方式:Class.forName("该类的完整包名")、类名.Class、对象.getClass()、获取到Class对象以后,可以根据该对象获取该类的构造方法、属性、方法、注解等。具体操作查看文档即可。java框架的配置文件就是通过Properties来读取,再通过反射创建相关对象。

30、动态代理

在java中,想要实现动态代理需要用到Proxy类和InvocationHandler接口,首先把需要被代理的类抽象成一个接口,创建一个需要被代理的类的对象,这个类需要实现该接口。然后再次创建一个类实现InvocationHandler接口,该接口需要设置一个属性,用来接收被代理的实例(通过构造函数设置即可,因为Invoke方法参数的缘故,需要将代理实例转换成Object)。该接口中有一个invoke方法需要实现(这个方法就是生成的动态代理类每次调用被代理的对象方法的方法,在此方法中还可以在调用的基础上作其他附加处理)。准备好这两个类后,通过Proxy的newProxyInstance(常用)方法来创建动态代理类。该方法接收三个参数。分别是类加载器、数组类型的代理接口、InvocationHandler的实例对象。通过这个方法返回一个Object对象,再将该对象转换成被代理的类型,就可以实现调用被代理的方法了,每次调用方法实际上调用的都是InvocationHandler接口的invoke方法。newProxyInstance方法返回的动态代理对象,Proxy还提供了一个返回动态代理类的方法getProxyClass(不常用)

31、Spring IOC实现原理

Spring框架通过容器来管理对象的生命周期,通过beanFactory来创建bean对象。beanFactory的实现原理就是简单工厂模式。一般的简单工厂模式需要传递用于区分的类的参数,这里需要将传参改造一下,改成反射的方式。通过传递的完整类名,以反射的方式来创建对象。beanFactory的实现思路如下:将编写好的xml配置文件通过代码进行解析(dom4j或者其他解析xml的工具)。将解析出来的完整类名传递给改造完的beanFactory,通过反射的方式进行创建对象。

32、Spring IOC与AOP

IOC翻译中文就是控制反转。传统程序中,一个类想要调用另一个类的方法时,需要通过new的方式在该类中创建这个类的对象,但是spring则是通过beanFactory来管理bean的生命周期,并以注入的方式将一个类注入到另一个类中。所以控制反转也称为依赖注入(DI)。AOP则是面向切面编程,就是将程序中通用的功能单独封装成一个切面。例如事务,然后通过声明的方式将切面配置到业务逻辑中。技术上主要就是java的动态代理。

33、mysql

mysql本身也具备缓存功能,在进行数据查询时,如果查询语句和之前的一致并且数据没有改动,就会直接去缓存取结果(mysql默认开启缓存)
mysql重要的两种锁机制:表锁、行锁。
mysql根据用途区分的两种锁:共享锁(读数据时使用)、排它锁(写数据时使用)
mysql乐观锁与悲观锁:
    1、乐观锁:先进行业务操作,在修改数据的最后一步时去拿锁(乐观的意思就是认为锁是一直存在的,在需要的时候直接取就可以了)
实现方式就是给数据添加一个版本号或者时间戳之类的东西,这样在修改数据时将版本号或者时间戳作为判断条件,只要条件满足就说明在这一时段没有
可其他事务操作该数据,这时获取写锁正常更新。
    2、悲观锁:先获取锁再进行业务操作。(就是悲观的认为获取锁大概率会失败的,所以要先获取,与java中的synchronized一致)
mysql事务特性:原子性、一致性、隔离性、持久性。
隔离级别:
    未提交读(在事务做修改操作时,即使没有提交,其他事务也能读取该事务修改后的数据,该事务因为某种原因回滚了,这时读到的数据
属于根本不存在的数据,就称为脏读);
    提交读(在事务中作任何操作对于其他事务来说都是不可见的,其他事务只能读取到该事务提交后或提交前的数据,也称不可重复读)
    可重复读(在某个事务中多次查询同样的数据,结果是一致的,该隔离级别可以解决脏读问题)
    可串行化(给事务强制加锁,并使事务串行,可能导致查询超时等问题)
mysql infodb存储引擎默认隔离级别为可重复读
一般如果需要支持事务、并且支持并发,直接使用infodb或Xtradb即可(infodb为mysql默认的存储引擎);
如果只是涉及select和insert可以考虑MyISAM(MyISAM会将数据存到内存中,然后等待操作系统定期将数据刷到磁盘上。这样可能会造成数据崩溃问题)

索引是存储引擎级别的,不同的存储引擎支持不同的索引,mysql大部分存储引擎都是b+tree,b+tree高度一般不超过3,并且每个叶子节点都存在指向
下一个叶子的指针。myisam和innoDB关于b+tree的区别在于一个索引存储的是数据的内存地址,另一个直接存储数据(也称作聚集索引)。所以innoDB
创建的表必须包含主键(主键相当于索引的key,innoDB存储引擎创建的表本身就是一个索引),myisam可以没有主键

你可能感兴趣的:(java基础,java)