封装、继承、多态。
-
封装
封装,就是把客观数据、操作封装起来,对外只开发接口方法,简单调用即可。封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细,,而只是要通过外部接口,以特定的访问权限来使用类的成员。
-
继承
继承,是指可以让某个类获得另一个的属性的方法。可以在已存在的类的基础上扩展产生新的类,并且无需修改原来的类。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。
-
多态
多态性是指允许不同子类型的对象对同一消息作出不同的响应,也就是说通过继承实现的不同对象调用相同的方法,进而有不同的行为。
要实现多态需要做两件事:
- 方法重写:子类继承父类并重写父类中已有的或抽象的方法;
- 对象造型:用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为。
- 面向过程就是分百析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
- 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为度了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
例如下五子棋,可以按照不同的思路来实现:
-
按照面向过程来设计:
1、开始游戏;2、黑子先走;3、绘制画面;4、判断输赢;5、轮到白子;6、绘制画面;7、判断输赢;8、返回步骤2;9、输出最后结果。
-
按照面向对象来设计:
1、黑白双方,这两方的行为是一模一样的;2、棋盘系统,负责绘制画面;3、规则系统,负责判定诸如犯规、输赢等。
-
类和接口
-
类:只能单继承,但是可以实现多接口
public class Test extends A implements AA, BB, CC {}
-
接口:可以多继承
public interface Test extends AA, BB, CC {}
-
类定义属性和方法,是指具体的类别,一个类不能既是水果类,又是动物类,所以只能单继承。
-
接口定义的是行为功能,一个类别可以拥有多个行为功能,所以可以实现多接口。
-
接口的多继承,可以看作是功能的扩展。
-
-
抽象类和接口
-
相同点
- 抽象类和接口都不能实例化,但可以定义抽象类和接口类型的引用。
- 抽象类中的抽象方法和接口中的方法,都不能有具体实现。(接口在Java8中可以有实现)
- 一个类如果继承了抽象类和接口,必须要对其中的抽象方法全部实现。(接口中方法默认的是public abstract修饰的)否则该类仍然需要被声明为抽象类。
-
不同点
-
抽象类可以有构造方法,抽象方法和具体方法。
接口不能有构造方法,而且其中的方法全部都是抽象方法(默认是public abstract修饰的)。
-
抽象类中的成员可以使private、默认、protected、public的。
接口中的成员全部都是public的。
-
抽象类可以定义成员变量。
接口中定义的成员变量其实都是常量,而且必须初始化赋值(接口中的成员变量默认是public static final 修饰的 )。
-
-
抽象类
- 有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。
- 抽象类的构造方法可以在子类中通过super调用,可以在抽象类的构造方法中完成所有子类都拥有的相同功能的初始化。
-
接口
public interface A { default String get(){ return "123"; } }
-
-
泛型是什么
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。实现参数的任意化。 泛型让编程人员能够使用类型抽象,通常用于集合里面(集合框架中的类都是泛型类)。
-
泛型的优点
在泛型之前,通过对类型Object的引用来实现"任意化",任意化的缺点就是需要强制转换,这种转换需要开发者对实际参数类型可以预知的情况下进行。对于强制转换错误的情况,编译期不会提示,运行时就会出现异常,这是一个安全隐患。
泛型的好处就是在编译期检查类型安全,并且所有强制转换都是自动和隐式的。提高代码重用率。
-
泛型的使用规则
- 泛型的类型参数只能是引用类型,不能是原始类型。因为泛型的本质是Object类型。
- 同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
- 泛型的类型参数可以有多个。
- 泛型的参数类型可以使用extends语句,例如。习惯上称为“有界类型”。
- 泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(“java.lang.String”);
-
泛型擦除
Java的泛型是伪类型,只存在与源码中,编译后的字节码中,就已经替换为原始类型,并且在相应的地方插入强制类型转换代码。对于运行期的Java而言,List< Integer>和List< String>是同一个类。
通过例子来正面类型擦除:
-
原始类型相等
public static void main(String[] args) { List<Integer> list1 = new ArrayList<>(); list1.add(1); List<String> list2 = new ArrayList<>(); list2.add("str"); System.out.println(list1.getClass() == list2.getClass()); }
一个List< Integer>数组,一个List< String>数组,最终getClass对比,返回的是true。说明Integer和String的类型都被擦除了,只剩下原始类型了。
-
通过反射添加其他元素
public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); list.getClass().getMethod("add", Object.class).invoke(list, "asd"); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } }
定义了一个Integer类型的ArrayList,直接调用add方法,只能添加int类型,但是通过反射,可以添加任意类型。说明Integer泛型在编译之后被擦除掉了,只保留原始类型。
原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。
-
-
泛型擦除带来的问题
- 强制类型转换
因为类型擦除的问题,所以所有的泛型类型变量最后都会被替换为原始类型。例如ArrayList的get方法:
public E get(int index) { RangeCheck(index); return (E) elementData[index]; }
虽然类型被擦除了,但是我们使用的时候,会自动强制类型转换。
如果我们通过反射插入了不一致的数据,那我们读取的时候,就会发生错误。