Java基础复习,整理知识点

最近开始复习Java基础,和Android,将知识点整理以下,另外开始尝试使用MarkDown写博客,写的有问题的地方还请各位包含。html

Android部分的内容整理见个人另外一篇博客java

1.Java关键字总结梳理

首先这里总结一下在编写类时经常会碰到的一些关键字:web

private,public,protected,default

关键字 同一个包中的其余类 不一样包中的其余类 子类 自身
private No No No Yes
protected Yes No Yes Yes
public Yes Yes Yes Yes
无修饰(default) Yes No No Yes


注意:以上几个修饰词是和包有关的面试

static

static关键字修饰内容的几个特色:
1. static修饰的变量和类文件一同被加载到内存中
2. 被修饰的方法能够直接经过类名加点来引用,也就是说static修饰部分的引用是不须要将对象实例化的。算法

有关static一些注意事项编程

  • static方法只能访问static变量
  • static方法中不能使用this,super这样的关键字,由于static优先于对象被加载到内存之中,static执行时对象可能还未被实例化。
  • 内部类包含static修饰的属性或方法时,内部类必须也被static修饰,其实理解起来也很简答,应为static会优先被加载,若是内部类不被static修饰,那么内部变量是不会被提早加载的,这时static关键字修饰就不起做用了。

final

  • final是一个修饰词,可修饰类,变量,函数
  • final修饰的类不可被继承
  • final修饰的函数没法被复写
  • final修饰的变量只能赋值一次

abstract

  • abstract一样是一个修饰词,可以修饰方法和类
  • abstract修饰的类没法被实例化,只可以经过子类的继承并实现内部全部的抽象函数才能被实例化。
  • abstract修饰的函数只须要申明方法名,参数,不须要写函数体。
  • 抽象类中一样能够定义非抽象的方法,同时抽象类也有构造函数,这个构造函数提供给子类实例化时使用。
  • 抽象类中也能够没有抽象的方法。
  • abstract不能够和static,private,final公用,简单理解一下,static修饰说明优先加载,而abstract未被实现,因此没法被优先加载。final修饰表名为最终状态没法修改,而abstract修饰的须要子类去实现,必须能够修改。private表示私有化,自由自身可以访问到,而abstract须要子类访问并实现函数体。

instanceof

用于判断类是否实现了指定接口或实现了指定的类,举个简单的例子:数组

public class Test {

    public static void main(String[] args) {

        NullPointerException e = new NullPointerException();
        System.out.println(e instanceof Exception);
    }
}

输出结果为true缓存

2.面向对象

面向对象最为主要的两个内容安全

  • 过程,其实也就是函数
  • 对象,则是对一些函数和属性进行了封装

因此,其实在面向对象的编程过程当中,编写类,也就是完成对函数和成员变量的封装(固然,在编写前须要对类进行设计)。数据结构

接口和接口的实现

  • 接口有interface关键字定义,内部函数默认为抽象的,因此接口没法实例化.
  • 一个类在实现了接口中全部的方法时才能被实例化,不然这个类仍是一个抽象类,没法被实例化,实现某个接口使用关键字implements
  • 一个类能够实现多个接口,并且接口之间也能够相互继承,而且接口能够多继承,一个简单的例子:
public class Test {

    interface interface1 {
        void function1();
    }

    interface interface2 {
        void function2();
    }

    interface interface3 extends interface1, interface2 {

    }

    class MyClass implements interface3 {

        @Override
        public void function1() {
            // TODO Auto-generated method stub

        }

        @Override
        public void function2() {
            // TODO Auto-generated method stub

        }

    }
}

能够看到,当MyClass实现了interface3,而且interface3继承了interface1和interface2时,MyClass须要实现interface1和interface2中的全部方法。

继承

继承当中子类与父类之间的一些关系:

  • 成员变量:当子类中出现与父类相同的成员变量时,在子类中调用优先调用子类的该变量,若是想要调用父类中的该成员,则须要使用super关键字。
  • 成员函数:当子类中出现与父类之中相同的方法时,子类的方法会将父类中的方法覆盖。在外部调用时,会调用子类的方法。
  • 构造函数:子类的构造函数中会默认调用super()意味着在构造子类以前须要先对其父类进行构造。

多态

举一个简单的多态例子:
父类定义

class Father {
    void sayHi()
    {
      System.out.println("father hello");
    }
}

子类定义:

class Son extends Father {
    void sayHi()
    {
      System.out.println("son hello");
    }
}

测试代码:

public static void main(String[] args) {

        Father f = new Son();
        f.sayHi();

    }

输出结果为“son hello”
能够看出,能够经过父类访问到子类的方法,这样为咱们操做大量对象提供了方便,当咱们须要操做大量不一样对象时,咱们只须要经过访问他们的父类来操做他们共性的一部分便可。
固然以上只是简单的多态的例子,动态在Java中有不少体现,接口的实现,继承,复写都体现着多态。

3.那些咱们经常遇到的类

Exception

异常是程序运行期间发生的不正常的行为,Java中将异常进行了封装,并可以使用try catch语句对异常捕获和处理。全部异常的父类都是Exception,这些异常类的共有特色就是可以被抛出。
这里对比一下throws和throw的区别:

throws抛出的是异常的类名,出如今方法头部,throws只是存在抛出异常的可能而且自身没法处理,须要交给调用者来处理。

private void main() {
        try {
            function();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void function() throws IOException {
        Socket s = new Socket("", 11);
    }

能够看到,咱们经过throws将异常抛出,而且在function被其余函数调用时,调用者任然须要对异常进行捕获和处理,固然调用者也能够继续讲异常抛出,等待其余的调用者对异常进行处理。

throw抛出的是异常的对象,出如今函数体内部,throw必定会抛出异常。看例子

public static void main(String[] args) {

        String real_pass_word = "123456768";
        String input_pass_word = "123456";

        try {
            if (!real_pass_word.equals(input_pass_word)) {

                throw new IOException();

            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

能够看到须要对当密码不相等时,咱们使用throw抛出了异常IOException,而且在外层经过try catch对异常进行了捕获。

Thread

首选明确一下线程和进程的区别:

进程是程序在执行当中分配资源和管理资源的最小单位。进程之间一般不会共享资源,每一个进程独有系统分配的资源,进程中能够包含多个线程。

线程是进程中的一个程序执行控制单元,同时也是一条执行路径,属于同一个进程的线程之间共享进程内的数据,而且能够相互之间进行通信。

建立一个新的线程有两种办法:
- 经过Thread或继承至Thread的类建立线程对象
- 经过实现Runable接口,并将实现Runable接口的对象做为参数传入Thread()构造方法中(这里解释一下Runable接口出现的缘由,因为,应为Java中类是单继承的,因此当一个类既想做为一个线程也有其余必需要继承的类时,就能够经过实现Runable接口来将须要做为线程执行的部分放入run方法之中)

这里有一个常见的面试题

public static void main(String[] args) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("哈哈哈");
            }
        }) {
            public void run() {
                System.out.println("呵呵呵");
            };
        }.start();
    }

上面的代码运行结果输出的“呵呵呵”,简单的解释一下,上述代码首先建立了一个Thread对象,此时run中输出“哈哈哈”,而后咱们经过Thread实现了一个匿名的内部类,这个类的run方法输出“呵呵呵”,最后运行输出的结果就是“呵呵呵”了。

多线程的好处固然是可以更好的利用cpu,可是多线程一样也带来了一些问题,因为同一个进程内的多个线程共享着进程内的资源,当多个线程同时运行时就容易出现共享资源的抢夺,一个常见的状况是多个线程在共同访问一个数据时,A线程正在操做数据,这个时候B线程进入并修改数据,这样会致使产生错误的结果,为了解决线程访问共享资源的问题,Java提供了关键字synchronized,如下提供一个synchronized关键的使用例子:

public class Test {

    public static void main(String[] args) {
        Example example = new Example();

        Thread t1 = new Thread1(example);
        Thread t2 = new Thread1(example);

        t1.start();
        t2.start();
    }

}

class Example {
    public synchronized void execute() {
        for (int i = 0; i < 10; ++i) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Hello: " + i);
        }
    }

}

class Thread1 extends Thread {
    private Example example;

    public Thread1(Example example) {
        this.example = example;
    }

    @Override
    public void run() {
        example.execute();
    }

}

上面的代码对于excute方法使用了synchronized修饰,执行结果为先打印一次0-9再打印一次0-9,而去除synchronized关键字后,会同步打印,输出结果为001122…..99。证实synchronized保证了函数被调用后执行完才能被其余调用者再次调用。

固然,处理synchronized方法之外,咱们一样可使用synchronized块来对小部分的代码进行同步,再来一个例子:

public class Test {

    private static Test instance;

    private Test() {
    }

    public static Test getInstance() {

        if (instance == null) {

            //锁是类的字节码文件,因为是静态方法
            synchronized (Test.class) {

                if (instance == null) {
                    instance = new Test();
                }

            }
        }

        return instance;
    }

}

上面的代码是一个典型的使用延迟加载的单例模式类,这种时候当多线程同事调用getInstance方法时,可能出现建立多个实体的状况,这样显然不符合单例模式的思想,因此咱们须要在建立实体时加上代码同步。为了不因为判断加锁和解锁的过程带来的低效,能够再同步部分再次添加判断,避免没有必要的同步。从Java 5 开始,推出了新的同步解决办法Lock
这部分的内容这里有一个不错的博文,推荐给你们,传送门点这里

String

String类,做为Java中使用最为频发的几个类之一,有必要好好熟悉一下,String类其实很简单,就是对字符串的封装,以及提供了不少方法方便咱们操做字符串。这里整理了一些很好用的,可是可能被你们忽略的方法:

方法 做用
charAt 获取指定位置的字符
indexOf 顺序获取字符或字符串的位置,没有返回-1
lastIndexOf 倒序获取字符或字符串的位置,没有返回-1
subString 获取指定位置的子串
contain 判断字符串是否包含指定字符串
startWith 判断字符串是否以指定字符串开头
endWith 判断字符串是否以指定字符串结尾
equalsIgnoreCase 判断字符串是否相等,忽略大小写
toLowerCase 全部字母转换为小写
toUpperCase 全部字母转换为大写
replace 替换
trim 去除字符串首位空格


总结完String的一些方法,这里再简单的提一下StringBuilder和StringBuffer两个类。String是赋值后没法修改的(咱们能够看到全部的关于String的操做返回结果都是一个新的String对象而不是原对象),而StringBuilder和StringBuffer是可修改的,这两个类做用相似都用于构建字符串,不一样在于:StringBuffer对于多线程是安全的,而StringBuilder对于多线程是不安全的。因此推荐的使用状况以下:
- 单线程操做时推荐使用StringBuilder,效率更高。
- 多线程时操做推荐使用StringBuffer,更加安全。

集合

Java中提供了功能强大的集合类,这里咱们对Java中的集合类进行整理:

  • Iterator (访问集合的迭代器接口)
  • Collection(单列)
    • List(有序,可存储重复元素,元素有索引)
    • Set(无序,不可存储重复元素,元素都是惟一的)
  • Map(双列)
    • Hashtable
    • TreeMap

List接口下咱们常常用到主要为如下几个类:

  • ArrayList: 底层数组实现,查询速度很快,线程不一样步
  • LinkedList:底层链表实现,增删熟读很快,线程不一样步
  • Vector:底层数组实现,查询增删速度都很慢,线程同步

在经过Iterator对List接口对象进行迭代时,若是想要对集合对象进行操做,会出现ConcurrentModificationException的异常,错误缘由是迭代过程当中Iterator已经在操做集合对象了,这时咱们再去操做集合对象会致使访问冲突。解决办法是使用iterator的子接口ListIterator,这个接口提供了对List集合对象的操做。

注意:因为在List集合中,判断元素是否存在或是删除元素都是经过元素的equals方法,因此在平常的开发过程当中,一般须要本身重写集合元素对象的equals方法,这样可以提升List集合的操做效率。

Set接口下咱们常常用到主要为如下几个类:
- HashSet:底层数据结构为Hash表,效率高,线程不一样步
- LinkedHashSet:HashSet的子类,有序
- TreeSet:底层数据结构为二叉树,能够对之中的数据进行指定顺序的排序,线程不一样步

简单介绍一下Hash表是什么:
1.对元素中特有数据进行Hash算法,并获得元素的Hash值
2.Hash值就是这个元素在表中的位置
3.在存储过程当中,若是Hash值发生冲突,则须要进行冲突解决,最简单的一个Hash冲突解决办法为再次判断元素是否相同,若是相同则不存储,若是不一样则存储,在原元素的Hash值的基础上加1。

(提供一个Hash冲突解决的博文连接

4.存储Hash值的结构称之为Hash表
5.为了提升效率,应该尽可能保证元素关键字的惟一性,这样可以提升Hash表的效率。

TreeSet中的元素须要是可比较的,为了保证元素可比较,须要元素实现Comparable接口,TreeSet中为保证元素的惟一,是经过Comparable接口的toCompare方法来实现的,当toCompare方法返回0时,表示两个元素相同。

Map
Map接口与Collection有很大的区别,Map一次存储一个键值对,而且须要保证Map中全部的健是惟一的。
迭代Map的方法:

  • 经过Map.keySet()获取健的Set,而后再遍历时经过getKey方法迭代
  • 经过Map.entrySet()方法获取到键值对set,直接遍历。

这里总结一下集合的使用规律:

当须要存储的是单个数据时考虑使用Collection,当须要存储的内容是键值对形式的数据使用Map

  • 须要保证内部元素惟一用Set,不要须要使用List
  • 看到Array说明底层数据结构是数组,证实查询速度快
  • 看到Linked说明底层数据结构是链表,增长删除的速度开
  • 看到Hash说明底层数据结构是Hash表,须要尽可能保证内部元素的Hash值惟一,而且须要复写元素的hasCode方法和equals方法。
  • 看到Tree就说明底层的数据结构是二叉树,须要至关排序,内部元素须要Comparable

泛型
泛型只是针对编译时期,在运行时期并不存在泛型的概念,泛型只是为了将一些类型强制转换的异常转化为编译错误。使用泛型时必须保证等式两边声明的泛型是同样的。
泛型的上限与下限:

  • ?extends E 泛型指 接受E和E的全部子类
  • ? super E 泛型指接受E和E的全部父类

IO Stream

  • 首先明确一下字符流和字节流的区别(这个问题今天早上把我一个电话面阿里的同窗给难住了,有必要好好记一下)

  • 字节流:字节流能够处理几乎计算机当中的全部数据(凡是以InputStream和OutputStream结尾的都为字节流)

  • 字符流:字符流的出现是应为,各个国家的语言不通,字符也不通,因此当将各总编码表和流封装在一块儿,为了方便字符的操做,因此设计到字符操做的时候优先考虑字符流(凡是以Reader和Writer结尾的都是字符流)

开发的时候如何明确该使用什么样的流呢:

  1. 若是须要读入数据使用InputStream和Reader,写入数据使用OutputStream和Writer
  2. 若是须要处理纯文本对象 使用Reader和Writer,否者使用InputStream和OutputStream
  3. 明确使用那个具体的流,经过明确具体操做的数据设备:(磁盘)File,(内存)CharArray。。。
  4. 是否须要利用缓存提升效率,若是须要可使用Buffer对流进再一次的封装。

Java部分到这里基本整理完毕了,4天的时间,也算是查漏补缺,感受的确再看一遍书又有了一些提升。这里整理的是我不太熟悉的一些知识点,并不必定全面,明天开始整理有关Android的部分。加油~!~~~