Java知识系列之基础内容

java三大特性

  1. 抽象
    1. 抽象类与接口的区别
  2. 封装:
    通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
  3. 多态:
    多态性是指允许不同子类型的对象对同一消息作出不同的响应。
    1. 静态多态:重载,编译期
    2. 动态多态:重写,运行时
    3. 重写要求:
      1. 父子类
      2. 方法名,与方法参数一致(参数个数,类型,顺序)
      3. 返回的数据类型必须是父类相同或者是其子类
      4. 访问修饰符必的限制必须要大于父类限制(public>protected>default>private)
    4. 重载要求:
      1. 同一个类;
      2. 方法名要相同
      3. 其他的,比如参数,返回类型,访问修饰符可以不同
  4. 继承
    继承是从已有类得到继承信息创建新类的过程。
    1. 内部类的区别
      1. 静态内部类
      2. 内部类
      3. 匿名内部类
    2. 内部类的作用
      1. 非静态内部类可以访问它的外部类对象的成员包括私有成员, 但是引用局部变量时,应该加final (为什么? 因为内部类和局部变量生命周期不同(方法结束后局部变量的生命周期就结束了,而内部类只要有引用就不结束,内部类的生命周期>=局部变量) Java为了解决这一问题,会在编译时在内部类的构造方法里边,将局部变量作为参数传入内部类 这就造成了局部变量如果改变,内部类不知情的场景,所以要加final,保证引用不可改变 在java8中,可以不使用final关键字,但是如果我们改变局部变量的引用,编译会发生错误,从而保证了局部变量的引用不变。)
    3. 内部类持有外部类引用: 答 (内部类虽然和外部类写在同一个文件中, 但是编译完成后, 还是生成各自的class文件,内部类通过this访问外部类的成员。编译器自动为内部类添加一个成员变量, 这个成员变量的类型和外部类的类型相同, 这个成员变量就是指向外部类对象。编译器自动为内部类的构造方法添加一个参数, 参数的类型是外部类的类型, 在构造方法内部使用这个参数为内部类中添加的成员变量赋值;在调用内部类的构造函数初始化内部类对象时,会默认传入外部类的引用。
      )

静态变量与成员变量的区别

  1. 静态变量属于类,被多个实例共享,成员变量属于实例
  2. 静态变量储存在方法区,成员变量在堆
  3. 静态变量在类加载时候存在,成员变量在实例加载之后存在
  4. 静态方法可以直接使用静态变量,不能直接使用成员变量;

抽像方法,是否可以同时是静态的?是否可以同时是本地方法?是否可以被synchronized修饰?

  1. 抽象方法不能是静态的,静态方法不能被子类重写,抽象方法必须被子类重写
  2. 抽象方法不能是本地的,本地方法是由本地代码实现,而抽象方法是没有具体实现
  3. 抽象方法不能用synchronized修饰; synchronized和方法实现的具体细节有关

Interface与abstract类的区别。

  1. 抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。

  2. 一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类。
    接口比抽象类更加抽象,因为抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的方法全部都是抽象方法。

  3. 抽象类中的成员可以是private、默认、protected、public的,而接口中的成员全都是public的。抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。

  • 抽象类是用来捕捉子类的通用特性的 ,是被用来创建继承层级里子类的模板,减少重复的代码

  • 接口本身就是提供的一种规范和约束。

  • 语法层面:

    • abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。
    • 在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的成员方法都是abstract的。
  • 设计理念:

    • abstract class表示的是"is-a"关系,interface表示的是"like-a"关系。
    • 抽象类是用来捕捉子类的通用特性的 ,是被用来创建继承层级里子类的模板,减少重复的代码
    • 接口本身就是提供的一种规范和约束。

java多态的实现原理:

答: 当jvm执行java字节码的时候,类型信息会存储在方法区中,为了优化对象的调用方法的速度,方法区的类型信息会增加一个指针,该指针指向一个记录该类方法的方法表,方法表中的每一个项都是对应方法的指针。

方法区:方法区和java堆一样,都是各个线程的共享内存,用于存储已经被jvm加载的类信息,常量,静态变量,即时编译器编译后的代码等数据

运行时常量池:他是方法区的一部分,class文件中除了类的信息(版本,方法,字段等描述信息)还有一个是常量池,用于存放编译器生成的各种符号引用,这部分信息在类加载时被存储在运行时常量池

方法的内存回收目标是:针对常量池的回收以及对类型的卸载

方法表的构造:由于java的单继承机制,一个类只能继承一个父类,所有的类又都继承Object类,方法表中最先存放的是Object的方法,接下来是父类的方法,最后是该类本身的方法。 如果子类改写了父类的方法,那么子类和父类那些同名的方法共享一个方法表项。由于这样的特性,使得方法表的偏移量总是固定的,例如,对任何类来说,其方法表的equals方法的偏移量总是一个定值,所有继承父类的子类的方发表中,其父类所定义的方法的偏移量也总是一个定值。

流程: 调用方法时,jvm通过对象引用 得到方法区类型信息的方发表的指针入口,查询类的方法表。根据实例方法的符号引用解析出该方法在方法表中的偏移量,子类对象声明为父类类型时,形式上调用的是父类的方法,此时jvm会从实际的方法表中找到该方法地址,从而定位到实际类的方法。

注:所有引用为父类,但是方法区的类型信息中存放的是子类的信息,所以调用的是子类的方发表。

反射机制

  1. JAVA反射机制是在运行状态中, 对于任意一个类, 都能够知道这个类的所有属性和方法; 对于任意一个对象, 都能够调用它的任意一个方法和属性; 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制.
  2. 主要作用有三:
    • 运行时取得类的方法和字段的相关信息。
    • 创建某个类的新实例(.newInstance())
    • 取得字段引用直接获取和设置对象字段,无论访问修饰符是什么。

泛型的优缺点

  1. 优点
    • 使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。
    • 泛型最常见的用途是创建集合类。
  2. 缺点
    • 在性能上不如数组快。

类加载与初始化的顺序

创建对象时构造器的调用顺序是: 父类静态初始化块 -> 子类静态初始化块 -> 父类初始化块 ->调用了父类构造器 -> 子类初始化块 -> 调用子类的构造器

java中的哪些关键字

  1. transient:用transient声明一个实例变量,当对象存储时,它的值不需要维持。换句话来说就是,用transient关键字标记的成员变量不参与序列化过程
  2. throws,throw,try,catch,finally
  3. final,finally,finalize
    1. final 修饰类,变量,方法 (类不能被继承,方法不可被重写,变量不可变)
    2. finally 异常捕获结构中; 该语句在及其特殊的情景下,可能不执行(调用了System.exit()方法;JVM崩溃了)
    3. finalize是Object中的方法,垃圾回收时会调用该方法.

自动装箱和自动拆箱

  1. Integer与内部类IntegerCache(如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象)
  2. char类型变量中能不能存储一个中文汉字? 答: char类型用来存储Unicode编码的字符,unicode编码字符集中包含了汉字,所以可以的

String与StringBuilder,StringBuffer的区别

  1. String: 只读,不可变; 在拼接String的时候,使用+编译器会帮我们进行优化
  2. StringBuilder: 可变,线程不安全,效率高 因为它的所有方面都没有被synchronized修饰 是JDK1.5引入的
  3. StringBuffer: 可变,线程安全,效率低
        String s1 = "Programming";
        String s2 = new String("Programming");
        String s3 = "Program";
        String s4 = "ming";
        String s5 = "Program" + "ming";
        String s6 = s3 + s4;
        System.out.println(s1 == s2); // false
        System.out.println(s1 == s5); //true
        System.out.println(s1 == s6); //false
        System.out.println(s1 == s6.intern()); //true
        System.out.println(s2 == s2.intern()); //false
解析:1. String是引用类型,这里 == 比较的是引用是否相同,即是否指向相同的地址
     2. 在new String对象时,会产生一个新的对象,并不会使用常量池中的字符串
     3. intern会在常量池中寻找该字符串(如果没有责新建),并返回他的地址

 String s = new String("xyz");创建了几个字符串对象?
 答: 两个对象,一个是静态区的"xyz";一个是用new创建在堆上的对象。
  1. String为什么设计成不变的?
    1. 线程安全,不可变天生线程安全
    2. String常被用作HashMap的key,如果可变会引有安全问题,如两个key相同
    3. String常被用作数据库或接口的参数,可变的话也会有安全问题
    4. 效率: 字符串池可以节省很多空间
    5. 效率: 每个String对应一个hashcode,再次使用的话不用重新计算

java中的编码

为什么需要编码
  1. 因为计算机存储信息的最小单元是一个字节 即8bit,所以能表示的范围是0~255,这个范围是无法保存所有的字符,所以需要一个新的数据结构char来表示这些字符,从char到byte需要编码.
常见的编码
  1. ASCII : 总共有 128 个,用一个字节的低 7 位表示,031 是控制字符如换行回车删除等;32126 是打印字符,可以通过键盘输入并且能够显示出来
  2. GBK: 码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。
  3. UTF-16 :UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因
  4. UTF-8:统一采用两个字节表示一个字符,虽然在表示上非常简单方便,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的今天,这样会增大网络传输的流量,而且也没必要。而 UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以是由 1~6 个字节组成。
java中需要编码的地方,一般都是在字符和字节的转换上,包括磁盘IO和网络IO
Reader类是 Java 的 I/O 中读字符的父类,而 InputStream 类是读字节的父类,InputStreamReader 类就是关联字节到字符的桥梁,它负责在 I/O 过程中处理读取字节到字符的转换,而具体字节到字符的解码实现它由 StreamDecoder 去实现,在 StreamDecoder 解码过程中必须由用户指定 Charset 编码格式。

Unicode与UTF-8的关系: unicode是字符集utf-8的一种编码方式,达到了对数据流压缩的目的

字节流与字符流的区别

  1. 字节流操作的都是字节byte, 字符流操作的都是Unicode字符
  2. 字节流不使用缓冲区, 字符流使用缓冲区 buffer
  3. 字节流通常用于处理二进制数据,实际上他可以处理任何类型的数据, 但它不支持直接写入或者读取Unicode码元; 字符流通常处理文本数据,它支持写入以及读取Unicode码元

IO与NIO的区别

  1. io面向流, nio面向缓冲区
  2. io是阻塞的, nio是非阻塞的? 有哪些方式处理非阻塞? TODO
  3. java nio的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来"筛选"通道; 这些通道里有已经可以处理的输入,或者选择已准备写入的通道.
1. 大文件的赋值: 利用NewIO的FileChanel

java中如何实现序列化? 有哪些意义?

  1. 序列化是将对象的状态信息转换为可以存储或传输的形式的过程
  2. java将对象序列化成了字节信息
  3. java通过Serializeable接口实现序列化

java中四种引用以及应用场景

  1. 强引用: 通常我们使用new操作符创建一个对象时所返回的引用即为强引用
  2. 弱引用: 若一个对象只能通过弱引用到达,那么它就会被回收(即使内存充足),同样可用于图片缓存中,这时候只要Bitmap不再使用就会被回收
  3. 软引用: 若一个对象只能通过软引用到达,那么这个对象在内存不足时会被回收,可用于图片缓存中,内存不足时系统会自动回收不再使用的Bitmap
  4. 虚引用: 虚引用是Java中最“弱”的引用,通过它甚至无法获取被引用的对象,它存在的唯一作用就是当它指向的对象回收时,它本身会被加入到引用队列中,这样我们可以知道它指向的对象何时被销毁

动态代理与静态代理

  1. 优点: 动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
  2. 缺点: 它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾。

Object有哪些公用方法?

1. clone方法
保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
主要是JAVA里除了8种基本类型传参数是值传递,其他的类对象传参数都是引用传递,我们有时候不希望在方法里将参数改变,这是就需要在类中复写clone方法。

2. getClass方法
final方法,获得运行时类型。

3.toString方法

该方法用得比较多,一般子类都有覆盖。

4.finalize方法

该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。

5.equals方法

该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。

6.hashCode方法

该方法用于哈希查找,可以减少在查找中使用equals的次数,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。

一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。

如果不重写hashcode(),在HashSet中添加两个equals的对象,会将两个对象都加入进去。

7.wait方法

wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。

调用该方法后当前线程进入睡眠状态,直到以下事件发生。

(1)其他线程调用了该对象的notify方法。

(2)其他线程调用了该对象的notifyAll方法。

(3)其他线程调用了interrupt中断该线程。

(4)时间间隔到了。

此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。

8.notify方法

该方法唤醒在该对象上等待的某个线程。

9.notifyAll方法

该方法唤醒在该对象上等待的所有线程。

hashCode的理解

以Java.lang.Object来理解,JVM每new一个Object,它都会将这个Object丢到一个Hash哈希表中去,这样的话,下次做Object的比较或者取这个对象的时候,它会根据对象的hashcode再从Hash表中取这个对象。这样做的目的是提高取对象的效率。具体过程是这样:

new Object(),JVM根据这个对象的Hashcode值,放入到对应的Hash表对应的Key上,如果不同的对象确产生了相同的hash值,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同hashcode的对象放到这个单链表上去,串在一起。

比较两个对象的时候,首先根据他们的hashcode去hash表中找他的对象,当两个对象的hashcode相同,那么就是说他们这两个对象放在Hash表中的同一个key上,那么他们一定在这个key上的链表上。那么此时就只能根据Object的equal方法来比较这个对象是否equal。当两个对象的hashcode不同的话,肯定他们不能equal.

Excption与Error区别

Error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的状况;
Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示如果程序运行正常,从不会发生的情况。

  1. Throwable
    Throwable是 Java 语言中所有错误或异常的超类。
    Throwable包含两个子类: Error 和 Exception 。它们通常用于指示发生了异常情况。
    Throwable包含了其线程创建时线程执行堆栈的快照,它提供了printStackTrace()等接口用于获取堆栈跟踪数据等信息。
  2. Exception
    Exception及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。
  3. RuntimeException
    RuntimeException是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。
    编译器不会检查RuntimeException异常。 例如,除数为零时,抛出ArithmeticException异常。RuntimeException是ArithmeticException的超类。当代码发生除数为零的情况时,倘若既"没有通过throws声明抛出ArithmeticException异常",也"没有通过try…catch…处理该异常",也能通过编译。这就是我们所说的"编译器不会检查RuntimeException异常"!
    如果代码会产生RuntimeException异常,则需要通过修改代码进行避免。 例如,若会发生除数为零的情况,则需要通过代码避免该情况的发生!
  4. Error
    和Exception一样, Error也是Throwable的子类。 它用于指示合理的应用程序不应该试图捕获的严重问题,大多数这样的错误都是异常条件。
    和RuntimeException一样, 编译器也不会检查Error。

Java将可抛出(Throwable)的结构分为三种类型:

  1. 被检查的异常(Checked Exception),Java编译器会检查它。 此类异常,要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。

  2. 运行时异常(RuntimeException) ,Java编译器不会检查它。 也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。

  3. 错误(Error)。和运行时异常一样,编译器也不会对错误进行检查。
    当资源不足、约束失败、或是其它程序无法继续运行的条件发生时,就产生错误。程序本身无法修复这些错误的。例如,VirtualMachineError就属于错误。

  4. OutOfMemoryError异常

除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能,
dump出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。

  1. 虚拟机栈和本地方法栈溢出
    如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

这里需要注意当栈的大小越大可分配的线程数就越少。
3. 运行时常量池溢出

异常信息:java.lang.OutOfMemoryError:PermGen space
如果要向运行时常量池中添加内容,最简单的做法就是使用String.intern()这个Native方法。该方法的作用是:如果池中已经包含一个等于此String的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。由于常量池分配在方法区内,我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量池的容量。

  1. 方法区溢出
    方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。

异常信息:java.lang.OutOfMemoryError:PermGen space

方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收,判定条件是很苛刻的。在经常动态生成大量Class的应用中,要特别注意这点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值