java的四个基本特性
抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这
些行为的的细节是什么。
继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类,基类);得到继承信息的类被称为子类(派生类)。
继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。
封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问智能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列
完全自治,封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据的数据操作的封装。可以说,封装就是隐藏
一切可隐藏的东西,只向外界提供最简单的编程接口。
多态:多态性是指允许不同子类型的对象对同一消息做出不同的响应。
多态的理解(多态的实现方式)
方法重载(overload)实现的是编译时的多态性(也称为前绑定)。发生在同一个类中,同名的方法有不同的参数列表
方法重写(override)实现的是运行时的多态(也称为后绑定)。运行时的多态是面向对象最精髓的地方。发生在父类与子类之间,子类不能比父
类声明更多的异常(里式代换原则)。子类方法的访问修饰符要大于父类。
实现多态需要做两件事情:1)方法重写(子类继承父类并重写父类中已有的或抽象的方法);2)对象造型(对父类性引用引用子类型对象,这样
同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,
所以概念上不适用。Java中也不可以覆盖private方法,因为private修饰的变量和方法只能在当前类中使用,如果是其他的类继承当前类是不能访问到
private变量或方法的,当然也不能覆盖。
Stack Queue
PriorityQueue是一个基于优先级堆的无界队列,它的元素是按照自然顺序排序的。在创建的时候,我们可以给它提供一个负责给元素排序的比较
器,PriorityQueue不允许null值,因为他们没有自然顺序,或者说他们没有任何相关联的比较器。最后,PriorityQueue不是线程安全的,入队和出队
的时间复杂度是O(log(n))。
1.添加元素add()和offer()
原理:添加元素位于末尾,同时队列长度加1,然后这个元素与它的父节点进行比较,如果比父节点小那么就与父节点进行交换,然后再与交换后的位置
的父节点进行比较,重复这个过程,知道钙元素的值大于父节点结束这个过程。
区别:两个方法都是向优先队列中插入元素,知识Queue接口规定二者对插入失败时的处理不同,add()在插入失败时抛出异常,offer()则会返回false。
2.寻找队列的头部元素element()和peek()头部元素,时间复杂度为1
两者语义完全相同,都是获取但不删除队首元素,二者唯一的区别是当方法失败时element()抛出异常,peek()返回null。
3.删除操作remove()和poll()
区别:两者的语义也完全相同,都是获取并删除队首元素,区别是当方法失败时,remove()抛出异常,poll()返回null。由于删除操作会改变队列
的结构,为维护小顶堆的性质,需要进行必要的调整。
原理:将队尾元素放到队首,然后对堆进行调整。
面向对象开发的六个基本原则(单一职责、开放封闭、里式替换、依赖倒置、合成聚合复用、接口隔离),迪米特法则。
六个基本原则
单一职责:一个类只做它该做的事情(高内聚),在面向对象中,如果只让一个类完成它该做的事,而不涉及与它无关的领域就是践行了高内聚
的原则,这个类就只有单一职责
开放封闭:软件实体应当对扩展开放,对修改关闭。要做到开闭有两个要点:1)抽象是关键,一个系统中如果没有抽象类或接口系统就没有
扩展点;2)封装可变性,将系统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一起,系统将变得复杂。
里式替换:任何时候都可以用子类型替换掉父类型。子类一定是增加父类的能力而不是减少父类的能力,因为子类比父类的能力更多,把能力多
的对象当成能力少的对象来用当然没有任何问题。
依赖倒置:面向接口编程(该原则说的直白和具体一些就是声明方法的参数类型、方法的返回类型。变量的引用类型时,尽可能使用抽象类型
而不是具体类型,因为抽象类型可以被它的任何一个子类型所替代)
合成聚合复用:优先使用聚合或合成关系复用代码。
接口隔离:接口要小而专,绝不能大而全。臃肿的接口是对接口的污染,既然接口表示能力,那么一个接口指引该描述一种能力,接口也应该是
高度内聚的。
迪米特法则:
迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解
Java创建对象的四种方法:
1.使用new关键字
2.使用Class类的newInstance方法,newInstance方法调用无参的构造器创建对象(反射)Class.forName.newInstance
3.使用clone方法
4.反序列化
四种方法中,使用构造器的三种:new和反射的两种newInstance。没用构造器的两种:clone和反序列化
String、StringBuffer、StringBuilder的区别
·都是final类,都不允许被继承
·String长度是不可变的,StringBuffer、StringBuilder长度是可变的
·StringBuffer是线程安全的,StringBuilder不是线程安全的,但他们两个中的所有方法都是相同的,StringBuffer在StringBuilder的方法之上
添加了synchronize修饰,保证线程安全。
·StringBuilder比StringBuffer拥有更好的性能。
·如果一个String类型的字符串,在编译时就可以确定是一个字符串倡廉,则编译完成之后,字符串会自动拼接成一个常量。此时String的速度
比StringBuffer和StringBuilder的性能好的多。
String不可变,final
·final
首先因为String不可变,如果不是final,那么就可以有子类继承String类,然后子类覆盖其方法,是的这些方法可以修改字符串,这样
就未被了String的不可变性
·不可变原因
1)提高效率:比如一个字符串String s1 = “abc”,"abc"被放到常量池里面去了,我再String s2 = “abc"并不会复制字符串"abc”,只会多个引用
指向原来那个常量,这样就提高了效率,二者一前提是string不可变,如果可变,那么多个引用指向同一个字符串常量,我就可以通过一个引用改变
字符串,然后其他引用就被影响了。
2)安全:string常被用来表示url,文件路径,如果string可变,或者存在安全隐患,就会变得不安全
3)string不可变,那么他的hashcode就一样,不用每次重新计算了
在通常情况下,String会重写Object的hashcode和toString。
在别人知道源码怎么实现的情况下,故意构造相同的hash的字符串进行攻击,怎么处理?那jdk怎么办?
·怎么处理构造相同的hash的字符串进行攻击?
·当客户端提交一个请求并附带参数的时候,web应用服务器会把我们的参数转换成一个HashMap存储。这个HashMap的逻辑结构如下:key1->value1;
·但是物理存储结构是不同的,key值会被转换成Hashcode,这个hashcode会被转换成数组的下标:0->value1;
·不同的string就会产生相同的hashcode而导致碰撞,碰撞后的物理存储结构可能如下:0->value1->value2;
·1 限制post和get的参数个数,越少越好。
2 限制post数据包的大小
3 WAF
·jdk7如何处理hashcode字符串攻击
HashMap会动态的使用一个专门的treemap实现来替换掉它。
在Java中,通过使用“+”符号来串联字符串的时候,实际上底层会转成通过StringBuilder实例的append()方法来实现。