面向对象基础概念
面向对象3大特点
封装:封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承:继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。
多态:多态是同一个行为具有多个不同表现形式或形态的能力。即同一个接口在,在不同的实例中执行不同的操作。
面向对象5大原则
单一职责原则:一个类或者方法应该只做一件事。
开放封闭原则:对象或实体应该对扩展开放,对修改关闭。
里式替换原则: 子类必须能够替换成它们的基类。继承关系要求子类具有基类的全部行为,如果子类继承基类后的行为无法满足基类中行为的期望要求,这个继承就是不正确的。例如子类鸵鸟继承基类鸟,鸟有fly()方法,但是鸵鸟继承了鸟之后却不能飞。
里氏代换原则就是在设计时避免出现派生类与基类不一致的行为。
依赖倒置原则:要依赖于抽象,不要依赖于具体。即要针对接口编程,而不是针对实现编程。
接口隔离原则:采用多个与特定客户类有关的接口比采用一个通用的涵盖多个业务方法的接口要好。具体业余在具体的接口中操作。
接口和抽象类的区别
抽象类主要是用来抽象类别,接口主要是用来抽象方法功能,当关注事物的本质时,使用抽象类,当关注事物的动作行为时使用接口。
Java接口中的成员变量默认为(public、static、final),成员方法为(public、abstract);抽象类中成员变量可以是任意类型的,可以包含抽象和非抽象方法。
注:JAVA 8中接口可以定义default 方法和静态方法,default和静态方法必须有方法体,静态方法不能被实现类重写,default可以被重写也可以不被重写,如果一个类实现了两个接口,这两个接口中定义了方法签名相同的default方法,则实现类中必须实现此default方法,否则编译器会报错,因为不知道使用哪个接口的default方法。default方法不能继承java.lang.Object中的方法,如toString(),这是避免多继承的问题,因为所有的类都继承自 Object,这样在接口和Object中有同签名的两个方法,且都有具体实现,无法知道要调用哪一个,一种解决方法是在实现此接口的时候实现toString(),但是这样在接口中定义default的toString()就没有意义了,因为始终会被重写。接口中不能定义与Object中方法签名相同的静态方法,因为静态方法是在编译阶段静态绑定的,而Object的toString()方法必须通过实例访问,在加载静态方法时不存在实例,所以静态方法不能重写Object中的方法。
一个类只能继承一个抽象类,但是可以实现多个接口。如果一个类继承自抽象类,则必须实现抽象类中的所有抽象方法,否则这个类也必须声明为抽象类;如果一个类实现了一个接口,则这个类必须实现接口中所有的方法,否则这个类必须声明为抽象类。
抽象类中可以有构造函数,接口中不允许定义构造函数。接口和抽象类都不允许直接实例化。
方法重写的原则
两同:方法名相同,参数类型相同
两小:子类返回类型小于父类返回类型、子类抛出异常小于父类抛出异常
一大:子类的访问权限大于父类的访问权限
JAVA 8 新特性
(1)、函数式编程:主要思想是把运算过程尽量写成一系列嵌套的函数调用。
函数式接口:在接口中定义一个唯一的抽象方法(可以有default和static方法),这个接口就成为函数式接口。使用@Functionalnterface注解接口,表示接口是函数式接口,若在接口中添加多于一个抽象方法,编译器会报错。
Lambda表达式:函数式接口可以使用Lambda表达式实例化,在Java 8之前使用匿名内部类来实例化接口,显得代码冗余。
Lambda表达式也成为闭包,允许把函数作为一个方法的参数。
(2)、Stream API:Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
(3)、在接口中支持default方法和静态方法。
回调函数、函数式编程、面向对象之间的区别
回调函数:
回调函数本质上是观察者模式,我们关注某个对象的结果,将自身的引用传递给这个对象,这个对象完成工作之后,调用我们的引用的方法。观察者模式有两个对象,观察者和事件源,事件源提供监听器接口,观察者实现此监听器接口,在使用时,观察者注册事件源的注册监听器函数,将自己的监听器接口实现传递给事件源,事件源在发生状态变化时,调用监听器接口的方法,观察者监听器实现中做具体的处理。
函数式编程:
函数式编程主要思想是把运算过程尽量写成一系列嵌套的函数调用。
函数式编程强调没有"副作用",意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。它不会依赖也不会改变当前函数以外的数据。(副作用指函数在调用过程中修改了全局变量)
函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快。
函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。
函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署"并发编程"(concurrency)。
面向对象:
在面向对象中,对象在绝大多数场合总是以一种可变的姿态出现,比如,从一个几个中选择几个满足要求的元素,一种做法是将不满足要求的元素删除,这样原来的集合就发生了变化。
匿名内部类访问外部类的局部变量为什么必须声明成final类型?
匿名内部类中使用外部类的局部变量,实际上将外部类的局部变量的引用拷贝了一份到匿名内部类里,这会带来不一致的情况:1、外部方法修改引用,导致内部类得到的引用值不一致;2、内部类修改引用,导致外部方法看不到修改后的值。使用final修饰来保证变量的不变性。Java 8中不需要显示地将局部变量声明为final了,但是依旧不可以修改。
public void test(final int test) {
// final int test = 0;
new InnerInterface() {
@Override
public int inner() {
System.out.println(test);
return 0;
}
}.inner();
}