java基础知识点


前言

java 基础知识总结

1.如何理解面向对象?

面向对象 (Object Oriented,OO) 的思想对软件开发相当重要,它的概念和应用甚至已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD 技术、人工智能等领域。面向对象是一种 对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物;
三大特性 封装,继承,多态(重载和重写)
六大设计原则 开闭原则、单一指责原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则。

  • 开闭原则(Open-Closed Principle, OCP):对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。不以改动原有类的方式来实现新需求,而是应该以实现事先抽象出来的接口(或具体类继承抽象类)的方式来实现
    优点:开闭原则的优点在于可以在不改动原有代码的前提下给程序扩展功能。增加了程序的可扩展性,同时也降低了程序的维护成本
  • 单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
    单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验
    优点:如果类与方法的职责划分的很清晰,不但可以提高代码的可读性,更实际性地更降低了程序出错的风险,因为清晰的代码会让bug无处藏身,也有利于bug的追踪,也就是降低了程序的维护成本
  • 依赖倒置原则(Dependency Inversion Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法
    依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。常用的注入方式有三种,分别是:构造注入,设值注入(Setter注入)和接口注入
    优点:通过抽象来搭建框架,建立类和类的关联,以减少类间的耦合性。而且以抽象搭建的系统要比以具体实现搭建的系统更加稳定,扩展性更高,同时也便于维护
  • 接口隔离原则(Interface Segregation Principle, ISP):使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。每一个接口应该承担一种相对独立的角色,不干不该干的事,该干的事都要干。
    接口仅仅提供客户端需要的行为,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口
    在使用接口隔离原则时,我们需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法
    优点:避免同一个接口里面包含不同类职责的方法,接口责任划分更加明确,符合高内聚低耦合的思想
  • 迪米特法则(Law of Demeter, LoD):一个软件实体应当尽可能少地与其他实体发生相互作用, 如果一个系统符合迪米特法则,那么当其中某一个模块发生修改时,就会尽量少地影响其他模块,扩展会相对容易,这是对软件实体之间通信的限制
    迪米特法则要求限制软件实体之间通信的宽度和深度。迪米特法则可降低系统的耦合度,使类与类之间保持松散的耦合关系。
    迪米特法则要求我们在设计系统时,应该尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那么这两个对象就不应当发生任何直接的相互作用,如果其中的一个对象需要调用另一个对象的某一个方法的话,可以通过第三者转发这个调用。简言之,就是通过引入一个合理的第三者来降低现有对象之间的耦合度。
    在将迪米特法则运用到系统设计中时,要注意下面的几点:在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;在类的设计上,只要有可能,一个类型应当设计成不变类;在对其他类的引用上,一个对象对其他对象的引用应当降到最低
    优点:实践迪米特法则可以良好地降低类与类之间的耦合,减少类与类之间的关联程度,让类与类之间的协作更加直接
  • 里氏替换原则(Liskov Substitution Principle, LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象,一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。
    在使用里氏代换原则时需要注意如下几个问题:
    (1)子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。根据里氏代换原则,为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,在父类中不提供相应的声明,则无法在以父类定义的对象中使用该方法。
    (2) 我们在运用里氏代换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。里氏代换原则是开闭原则的具体实现手段之一
    优点:可以检验继承使用的正确性,约束继承在使用上的泛滥。

2.面向对象和面向过程有什么区别?

面向对象 (Object Oriented,OO) 的思想对软件开发相当重要,它的概念和应用甚至已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD 技术、人工智能等领域。面向对象是一种 对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物;
面向过程 (Procedure Oriented) 是一种 以过程为中心 的编程思想。这些都是以什么正在发生为主要目标进行编程,不同于面向对象的是谁在受影响。与面向对象明显的不同就是 封装、继承、类

3. Java面向对象的三大特性?

三大特性:封装,继承,多态(重载和重写)
面向对象编程是利用 类和对象编程的一种思想。万物可归类,类是对于世界事物的高度抽象 ,不同的事物之间有不同的关系 ,一个类自身与外界的封装关系,一个父类和子类的继承关系, 一个类和多个类的多态关系。万物皆对象,对象是具体的世界事物,面向对象的三大特征封装,继承,多态,封装,封装说明一个类行为和属性与其他类的关系,低耦合,高内聚;继承是父类和子类的关系,多态说的是类与类的关系

  • 封装:封装是将代码及其处理的数据绑定在一起的一种编程机制,该机制保证了程序和数据都不受外部干扰且不被误用。封装的目的在于保护信息
    Java 语言的基本封装单位是类。由于类的用途是封装复杂性,所以类的内部有隐藏实现复杂性的机制。Java 提供了私有和公有的访问模式,类的公有接口代表外部的用户应该知道或可以知道的每件东西,私有的方法数据只能通过该类的成员代码来访问,这就可以确保不会发生不希望的事情
    在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。
    封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
    要访问该类的代码和数据,必须通过严格的接口控制。
    封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
    适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
    封装的优点
    良好的封装能够减少耦合。类内部的结构可以自由修改。可以对成员变量进行更精确的控制。隐藏信息,实现细节。
    Java 封装,说白了就是将一大坨公共通用的实现逻辑玩意,装到一个盒子里(class),出入口都在这个盒子上。你要用就将这个盒子拿来用,连接出入口,就能用了,不用就可以直接扔,对你代码没什么影响
  • 继承:子类拥有父类数据结构的方法和机制,这是类之间的一种关系;继承只能是单继承但是可以通过内部类继承其他类来实现多继承。
    将多个类的通用属性和方法提取出来,放在它们的父类中,然后只需要在子类中各自定义自己独有的属性和方法,并以继承的形式在父类中获取它们的通用属性和方法。
  • 多态:面向对象的多态性,即“一个接口,多个方法”。多态性体现在父类中定义的属性和方法被子类继承后,可以具有不同的属性或表现方式。多态性允许一个接口被多个同类使用,弥补了单继承的不足
    多态优点:可替换性、可扩充性、接口性、灵活性、简化性
    向上转型:指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法
    调用的优先级方法,该优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。多态的实现原理

4.Java中重载和重写

  • 重载(overloading):编译时多态, 在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同
    重载规则:
    1、被重载的方法必须改变参数列表(参数个数或类型不一样);
    2、被重载的方法可以改变返回类型;
    3、被重载的方法可以改变访问修饰符;
    4、被重载的方法可以声明新的或更广的检查异常;
    5、方法能够在同一个类中或者在一个子类中被重载。
    6、无法以返回值类型作为重载函数的区分标准
  • 重写(Override):运行时多态,如果一个子类继承了一个父类,子类中拥有和父类相同方法名称,返回值,参数类型的话,就是重写,会执行子类中的方法,子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写
    方法的重写规则
    1、参数列表与被重写方法的参数列表必须完全相同。
    2、返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
    3、访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
    4、父类的成员方法只能被它的子类重写。
    5、声明为 final 的方法不能被重写。
    6、声明为 static 的方法不能被重写,但是能够被再次声明。
    7、子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
    8、子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
    9、重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
    10、构造方法不能被重写。
    11、如果不能继承一个类,则不能重写该类的方法

5.java中的访问修饰符

Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限

  • default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法
  • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
  • public : 对所有类可见。使用对象:类、接口、变量、方法
  • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

6. Java的8种基本类型与封装类?

一个字节8位

  • **整数类型:**字节型(byte/8位)、短整型(short/16位)、整型(int/32位)、长整型(long/64位)
  • **浮点类型:**单精度型(float/32位)和双精度类型(double/64位)
  • **字符类型:**字符类型(char/16位)
  • **布尔类型:**布尔类型(boolean/1位),常量只有“真(true)”和“假(false)”这两个状态
  • Java中的封装类:
    8种基本类型按照类型划分:byte,short,int,long,float,double,boolean,char。
    8种基本类型的封装类:Byte,Short,Integer,Long,Float,Double,Boolean,Character.
    为什么需要封装类?
    因为泛型类包括预定义的集合,使用的参数都是对象类型,无法直接使用基本数据类型,所以Java又提供了这些基本类型的封装类。
    基本类型和对应的封装类由于本质的不同。具有一些区别:
    1.基本类型只能按值传递,而封装类按引用传递。
    2.基本类型会在栈中创建,而对于对象类型,对象在堆中创建,对象的引用在栈中创建,基本类型由于在栈中,效率会比较高,但是可能存在内存泄漏的问题
    注意点:
    包装类的默认值为 null,包装类可以区分出未赋值和值为 0 的区别,而数据类型无法表达出未赋值的情况。
    基础数据类型在传递参数时都是按值传递,封装类都是按引用传递。
    JAVA中的数值类型不存在无符号的,它们的取值范围是固定的,不会随着机器硬件环境或者操作系统的改变而改变。
    Java默认声明的小数类型是 double 类型的,所以在声明 float 类型的小数时需要进行类型转换。(float f = 1.0f 或者 float f = (float)1.0 )
    基本数据类型之间可以进行相互转换,有两种转换方式,分别是自动转换和强制转换,自动转换是从低精度向高精度转换,优先级顺序为:byte<short<char<int<long<float<double。强制转换则是从高精度向低精度进行转换,在转换过程中可能会损失精度。(自动转换:int a = 10; double b = a; b的值为10.0)
    byte, char, short 类型的数据在参与运算时会自动转换为int类型,但当使用’'+=" 运算符时就不会产生类型转换,例如 short s1= 1; s1=(shot) (s1+1) 。short s2 = 1;s1+=1
    基本数据类型不能与boolean类型相互转换
    直接赋值时,都会调用valueOf方法。所以要注意其的源代码,例:Integer i = 10;
    Character
public static Character valueOf(char c) {
        if (c <= 127) { // must cache
            return CharacterCache.cache[(int)c];
        }
        return new Character(c);
    }

Byte

public static Byte valueOf(byte b) {
        final int offset = 128;
        return ByteCache.cache[(int)b + offset];
    }

Short

public static Short valueOf(short s) {
        final int offset = 128;
        int sAsInt = s;
        if (sAsInt >= -128 && sAsInt <= 127) { // must cache
            return ShortCache.cache[sAsInt + offset];
        }
        return new Short(s);
    }

Integer

 public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

Long

public static Long valueOf(long l) {
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }

Double

public static Double valueOf(double d) {
        return new Double(d);
    }

Float

public static Float valueOf(float f) {
        return new Float(f);
    }

Boolean

public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

7. Java中“==”和equals的区别?

  • 基本型和基本型封装型进行==运算符的比较,基本型封装型将会自动拆箱变为基本型后再进行比较,因此Integer(0)会自动拆箱为int类型再进行比较,显然返回true
  • 两个Integer类型进行==比较,如果其值在-128至127,那么返回true,否则返回false, 这跟Integer.valueOf()的缓冲对象有关,这里不进行赘述
  • 两个基本型的封装型进行equals()比较,首先equals()会比较类型,如果类型相同,则继续比较值,如果值也相同,返回true;如果类型不同,则返回false
  • 基本型封装类型调用equals(),但是参数是基本类型,这时候,先会进行自动装箱,基本型转换为其封装类型,再进行两个基本型的封装型进行equals()比较

8. 为什么重写equals一定要重写hashcode?

当一个对象类只重写equals不重写hashcode时,会导致对象的hashcode继承object的hashcode方法根据地址获取hashcode在进行equals比较时会出现两个对象值相等hashcode不相等,因此重写equals一定要重写hashcode
hashcode的一些规定
两个对象相等,hashcode一定相等
两个对象不等,hashcode不一定不等
hashcode相等,两个对象不一定相等
hashcode不等,两个对象一定不等

9. Java中抽象类和接口的区别?

  • 抽象类(abstract ):
    抽象方法是一种特殊的方法:它只有声明,而没有具体的实现 abstract void fun();
    抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象
    在《JAVA编程思想》一书中,将抽象类定义为“包含抽象方法的类”,但是后面发现如果一个类不包含抽象方法,只是用abstract修饰的话也是抽象类。也就是说抽象类不一定必须含有抽象方法
    包含抽象方法的类称为抽象类,但并不意味着抽象类中只能有抽象方法,它和普通类一样,同样可以拥有成员变量和普通的成员方法。注意,抽象类和普通类的主要有三点区别:
      1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
      2)抽象类不能用来创建对象;
      3)如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。
      4)抽象类不能被实例化,如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
    在其他方面,抽象类和普通的类并没有区别
  • 接口(interface):
    1、接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
    2、接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
    3、接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
为什么接口中的成员变量非得是public static final的呢?
首先明白一个原理,就是接口的存在意义。接口就是为了实现多继承的抽象类,是一种高度抽象的模板、标准或者说协议。规定了什么东西该是这样,如果你继承了我这接口,就必须这样。比如USB接口,就是小方口,两根电源线和两根数据线,不能多不能少。
public: 使接口的实现类可以使用这个常量
static:static修饰就表示它属于类的,随的类的加载而存在的,如果是非static的话,就表示属于对象的,只有建立对象时才有它,而接口是不能建立对象的,所以接口的常量必须定义为static。
final:final修饰就是保证接口定义的常量不能被实现类去修改,如果没有final的话,
由子类随意去修改的话,接口建立这个常量就没有意义了。
  • 接口与抽象类的区别
    1.抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
    2.抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
    3.接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
    4.一个类只能继承一个抽象类,而一个类却可以实现多个接口。
    注:JDK 1.8 以后,接口里可以有静态方法和方法体了。

10. final finally finalize 区别及用法

  • final:用于声明方法,属性和类,被声明的方法不能覆盖不能重载,但是不影响被继承,类不能被继承,属性不可改变
  • finally:表示总是执行与try一起使用 return、continue和break都没能阻止finally语句块的执行 。编译器在编译return new ReturnClass();时,将它分成了两个步骤,new ReturnClass()和return,前一个创建对象的语句是在finally语句块之前被执行的,而后一个return语句是在finally语句块之后执行的,也就是说finally语句块是在程序退出方法之前被执行的。同样,finally语句块是在循环被跳过(continue和中断(break之前被执行的;
  • finalize:object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收;

11.this和super的区别

  • this:当前对象 区别当前对象属性与形参 this.name=name 调用构造函数 this()且一个方法只能调用一次
  • super:父类对象 super()调用父类的构造函数,调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错

12.泛型

泛型表示不明确定义一个类,可以在创建对象或者调用方法的同时确定这个对象中使用类的类型,类似于形参传递,静态方法不能使用类的泛型而应该将该方法定义为
静态泛型方法 static T xxx(T t){return t;}
泛型普通方法

public class Animal {
    public void getAnimalType(){
        Dog dog= getAnimal(new Dog());
        System.out.println(dog.getType());
    }
    public <T> T getAnimal(T t){
        return t;
    }
}

泛型类

public class Animal<T> {
    public T getAnimal(T t){
        return t;
    }
}

泛型的优点:类型安全 指定类型之后 类型限制可避免转换异常也可规范代码

13.JDK1.8中有哪些新特性?

  • 1、Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法
public interface People {
    void run();
    default String speak() {
        return "会说普通话!";
    }
}
  • 2、Lambda 表达式
    把函数作为一个方法的参数(函数作为参数传递到方法中)
public class Lambda {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        Collections.sort(list,(a,b)->{
            return a.compareTo(b);
        });
    }
}
  • 3、Java 8 允许你使用 :: 关键字来传递方法或者构造函数引用
  • 4、Lambda 作用域:在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量
  • 5、我们可以直接在lambda表达式中访问外层的局部变量不需要吧变量声明为final
  • 6、lambda内部对于实例的字段以及静态变量是即可读又可写
  • 7、Lambda表达式中是无法访问到默认方法的
    Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法
    Supplier 接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数
    Consumer 接口表示执行在单个参数上的操作
    Comparator 是老Java中的经典接口, Java 8在此之上添加了多种默认方法
    Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional
    java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。 Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行
  • 8、date api
    Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。某一个特定的时间点也可以使用Instant类来表示,Instant类也可以用来创建老的java.util.Date对象
    Timezones 时区在新API中时区使用ZoneId来表示。时区可以很方便的使用静态方法of来获取到。 时区定义了到UTS时间的时间差,在Instant时间点对象到本地日期对象之间转换的时候是极其重要的
    LocalTime 定义了一个没有时区信息的时间,例如 晚上10点,或者 17:30:15。下面的例子使用前面代码创建的时区创建了两个本地时间。之后比较时间并以小时和分钟为单位计算两个时间的时间差
    LocalDate 表示了一个确切的日期,比如 2014-03-11。该对象值是不可变的,用起来和LocalTime基本一致。下面的例子展示了如何给Date对象加减天/月/年。另外要注意的是这些对象是不可变的,操作返回的总是一个新实例
    LocalDateTime 同时表示了时间和日期,相当于前两节内容合并到一个对象上了。LocalDateTime和LocalTime还有LocalDate一样,都是不可变的。LocalDateTime提供了一些能访问具体字段的方法
  • 9、Annotation 注解:在Java 8中支持多重注解,Java 8允许我们把同一个类型的注解使用多次

14.JDK, JRE 和JVM的区别

  • JDK(Java Development Kit)简单理解就是Java开发工具包,JRE(Java Runtime Enviroment)是Java的运行环境,JVM( java virtual machine)也就是常常听到Java虚拟机。JDK是面向开发者的,JRE是面向使用JAVA程序的用户。
    Java 开发工具包 (JDK)
    Java开发工具包是Java环境的核心组件,并提供编译、调试和运行一个Java程序所需的所有工具,可执行文件和二进制文件。JDK是一个平台特定的软件,有针对Windows,Mac和Unix系统的不同的安装包。可以说JDK是JRE的超集,它包含了JRE的Java编译器,调试器和核心类。目前JDK的版本号是1.7,也被称为Java 7。
  • Java虚拟机(JVM)
    JVM是Java编程语言的核心。当我们运行一个程序时,JVM负责将字节码转换为特定机器代码。JVM也是平台特定的,并提供核心的Java方法,例如内存管理、垃圾回收和安全机制等。JVM 是可定制化的,我们可以通过Java 选项(java options)定制它,比如配置JVM 内存的上下界。JVM之所以被称为虚拟的是因为它提供了一个不依赖于底层操作系统和机器硬件的接口。这种独立于硬件和操作系统的特性正是Java程序可以一次编写多处执行的原因。
  • Java运行时环境(JRE)
    JRE是JVM的实施实现,它提供了运行Java程序的平台。JRE包含了JVM、Java二进制文件和其它成功执行程序的类文件。JRE不包含任何像Java编译器、调试器之类的开发工具。如果你只是想要执行Java程序,你只需安装JRE即可,没有安装JDK的必要。
  • JDK, JRE 和JVM的区别
    JDK是用于开发的而JRE是用于运行Java程序的。
    JDK和JRE都包含了JVM,从而使得我们可以运行Java程序。
    JVM是Java编程语言的核心并且具有平台独立性。

15.IO流

java.io.File 类是专门对文件进行操作的类,只能对文件本身进行操作,不能对文件内容进行操作可以对文件进行创建,查找和删除操作等,注意file的构造方法对传入的路径不做要求因为在创建对象的时候不会去检测这个路径是否存在,路径是不区分大小写的;

  • 根据数据的流向分为:输入流输出流
    1、输入流:把数据从设备上读取到内存
    2、输出流:把数据从内存输出到指定设备上
  • 根据数据的类型分为:字节流字符流
    1、字节流:以字节为单位,读写数据
    2、字符流:以字符为单位,读写数据
    在这里插入图片描述

字节输出流(outputStream)将数据输出
文件输出流(fileOutputStream)将数据输出到文件中 (注:new FileOutputStream(“fos.txt”,true);追加续写在原有文件数据基础上追加数据)
字节输入流(inputStream)将数据读取到内存
文件输入流(fileInputStram)将文件中的数据读取到内存

  • 字符流
    因为数据编码的不同,因而有了对字符进行高效操作的流对象,字符流本质其实就是基于字节流读取时,去查了指定的码表,而字节流直接读取数据会有乱码的问题
    字符为单位读写数据,字符流专门用于处理文本文件。如果处理纯文本的数据优先考虑字符流
    字符输入流 Reader 抽象类字符输入流的父类
    文件字符输入流FileReader( 继承 inputStreamReader) 文件输入流 构造时使用系统默认的字符编码和默认字节缓冲区
    read()每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回-1,循环读取
    字符输出流(writer 所有字符输出流的父类)
    文件字符输出流 (FileWriter 继承 outputStreamWriter) 文件字符输出流将文件输出(可以续写FileWriter (文件地址,true))
    writer()一次输出一个字符,如果输出流不关闭,数据只是保存到缓冲区,并未保存到文件,因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush 方法了
    flush :刷新缓冲区,流对象可以继续使用。
    close:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了
    注意:如果我们写入数据之后没有flush直接关闭输出流可能会导致数据丢失,因为我们在写入的时候是把数据先读取到内存再把数据写出,当我们读取完成时不一定写入也完成,有可能会有一部分数据在缓存区未被写入赭石直接关闭输出流会导致缓存区中的数据无法写出导致数据丢失,这个时候就需要flush函数用于清空缓冲区的数据流,保证缓冲区无数据全部写出
  • 缓冲流
    缓冲流也叫增强流对文件输入输出流的增强高效的io操作,将读取到的数据存到缓存区中然后在对缓存区的数据写出这样可以减少io操作大大提高效率
    字节缓冲流:BufferedInputStream,BufferedOutputStream
    字符缓冲流:BufferedReader,BufferedWriter
  • 转换流
    将字符转为字节,将字节转为字符
    InputStreamReader从字节流转为字符流 可以指定字符集 默认UTF8编码
    InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName) , “GBK”)
    isr.read()
    OutputStreamWriter从字符流转为字节流 可以指定字符集将字符编码为字节 默认UTF8编码
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“b.txt”) , “GBK”);
    osw.write(“111”)
  • 序列化流
    ObjectOutputStream将Java对象的原始数据类型写出到文件,实现对象的持久存储(对象要实现序列化接口java.io.Serializable)
    ObjectOutputStream(FileOutputStream out)弱项持续写入 fileOutputStream(文件,true)
    ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象
    ObjectInputStream(FileInputStream fis)
    注意: 在持久化对象到本地文件时在写入对象是会有一个header 若流关闭之后再次对文件进行追加写入,读取对象会导致发生异常,我们就需要重写 ObjectOutputStream 中的 writeStreamHeader 方法,不让它再写入头部信息 使其在非第一次写入的时候不加入header
    重写writeStreamHeader (){super.reset()}

bio,nio,aio的区别

  • bio:block io,同步阻塞式IO,就是我们平常使用的传统IO,他的特点是模式简单使用方便,并发处理能力低,
    :::BIO (Blocking I/O):同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的I/O并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的BIO模型是无能为力的。因此,我们需要一种更高效的I/O处理模型来应对更高的并发量
  • nio:Non io 同步非阻塞IO,是传统IO的升级,客户端和服务端通过Channel(通道)通讯,实现了多路复用
    :::NIO (New I/O):NIO是一种同步非阻塞的I/O模型,在Java 1.4中引入了NIO框架,对应java.nio包,提供了Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。NIO提供了与传统BIO模型中的Socket和ServerSocket相对应的SocketChannel和ServerSocketChannel两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用NIO的非阻塞模式来开发
  • AIO:asynchronous IO 是NIO的升级,也叫NIO2,实现了异步非阻塞IO,异步IO的操作基于事件和回调机制
    :::AIO (Asynchronous I/O):AIO也就是NIO 2。在Java 7中引入了NIO的改进版NIO 2,它是异步非阻塞的IO模型。异步IO是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO是异步IO的缩写,虽然NIO在网络操作中,提供了非阻塞的方法,但是NIO的IO行为还是同步的。对于NIO来说,我们的业务线程是在IO
    操作准备好时,得到通知,接着就由这个线程自行进行IO操作,IO操作本身是同步的。查阅网上相关资料,我发现就目前来说AIO的应用还不是很广泛,Netty之前也尝试使用过AIO,不过又放弃了

16.java反射

类:具有相同特性和行为,执行时具有不同状态的数据类型;
如果我们不知道一个对象的准确类型,而这个类在编译期也无法得知确切的类型,我们就可以使用“反射”,反射提供了一种机制可以获取到这个未知类型对象的可用方法,并产生方法名在运行期查询对象的信息。
Class类支持“反射”的概念:Field,Method 以及 Constructor 类这些类型都是代表未知的类里对应的成员,这样便可用于构建器创建新对象,用 get()和 set()方法读取和修改与 Field 对象关联的字段,以及用 invoke()方法调用与 Method 对象关联的方法。此外,我们可调用方法 getFields(),getMethods(),getConstructors(),newInstance()分别返回用于表示字段、方法以及构建器的对象数组与对象。因此,匿名对象的类信息可在运行期被完整的揭露出来,而在编译期间不需要知道任何东西。
反射获取类的成员变量若成员变量为private修饰则使用class.getDeclaredField(具体成员变量名称)
或者class.getDeclaredFields()获取所有的field 若需对变量进行操作需field.setAccessible(true)使变量可操作 若为public 则使用class.getField(名称)

17.java初始化顺序

  • 当对象首次创建或者首次被调用对象的static方法或者变量时,java解释器会先找到类.class
  • 找到类.class之后它的所有的static初始化模块都会运行一次,因此static指挥初始化一次就是在class首次加载的时候
  • 当创建对象new对象的时候会在堆中分配一块存储空间 这时存储空间会把对象中的所有基本类型按照基类到导出类依次都赋值为默认值
  • 然后再依次按照基类-导出类的顺序对字段进行初始化

18.static的理解

static可以修饰变量,方法

  • 修饰的变量为静态变量为类所有,不属于任何实例对象,所有实例共享的只会在类首次加载的时候加载,在内存中只存在一个副本,在类加载过程中,jvm只为静态变量分配一次内存空间

19.匿名内部类

匿名内部类就是没有名字的内部类

  • 匿名内部类必须继承一个抽象类或者实现一个接口
  • 匿名内部类不能定义静态成员和静态方法
  • 当所在方法的形参要被匿名内部类使用时必须声明为final
  • 匿名内部类不能是抽象的,他必须实现继承的类或者实现接口的所有抽象方法
    优点
  • 一个内部类对象可以访问创建他的外部类对象的内容,包含私有数据
  • 内部类不为同一包的其他类所见,具有很好的封装性
  • 内部类实现了多重继承,优化java单继承的缺陷
  • 匿名内部类可以很方便的定义回调
    匿名内部类的引用的形参为什么是final的?
    final修饰的变量是不能改变的,当匿名内部类调用方法的变量时其实是内部类的构造方法复制局部变量为内部类的成员变量,然后方法再进行调用,若外部变量可变就会导致内外变量数值出现不一致的情况,有可能会被外部的方法改变变量的值

20.java的值传递

java只有值传递
值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了
引用传递:指的是在方法调用时,传递的参数是按照引用进行传递,其实传递的引用的地址指的是在法调用时,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)
在Java中方法调用时是将传来的参数拷贝一份,引用地址也拷贝一份,当拷贝的引用地址指向别处时是互不影响的

21.代理

-动态代理:代理类在程序运行时创建的代理方式被成为动态代理。也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数
:::jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。。
jdk动态代理是jdk原生就支持的一种代理方式,它的实现原理,就是通过让target类和代理类实现同一接口,代理类持有target对象,来达到方法拦截的作用,这样通过接口的方式有两个弊端,一个是必须保证target类有接口,第二个是如果想要对target类的方法进行代理拦截,那么就要保证这些方法都要在接口中声明,实现上略微有点限制
:::为什么使用代理类
可以在不修改别代理对象代码的基础上,通过扩展代理类,进行一些功能的附加与增强
:::静态代理与动态代理的区别
动态代理使我们免于去重写接口中的方法,而着重于去扩展相应的功能或方法的增强,
与静态代理相比简单了不少,减少了项目中的业务量
:::动态代理的机制
Proxy这个类的作用就是用来动态创建一个代理对象的类。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值