抽象类的作用是什么?
抽象类不能创建实例,只能当成父类来继承。从语义的角度来说,抽象类是从多个具体类中抽象出来的父类,他具有更高层此的抽象。从多个具有相同特征的类中抽象出了一个抽象类,以这个抽象类作为其子类的模板,从而避免了子类设计的随意性。
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展,改造,但子类总体上会大致保留抽象类的行为方式。
什么是接口?
在java8里,允许为接口定义默认方法,类方法。
接口从宏观上来说,是从多个相似类中抽象出来的规范,接口不提供任何实现。说简单点,接口反映的是一类事物的方法。
接口里可以包含成员变量(只能是静态常量),方法(只能是抽象实例方法,类方法或默认方法),内部类(内部接口,枚举)
接口支持多继承。
谈谈你对接口和抽象类的理解?
接口和抽象类具有如下相同特征:
- 接口和抽象类都不能被实例化,他们都位于继承树的顶端,用于被其他类实现和继承。
- 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。
- 但接口和抽象类之间的差别非常大,这种差别主要体现在二者的设计目的上。
- 接口体现的是一种规范。对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务(以方法的形式提供);对于接口的调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用这些服务(就是如何来调用方法)。当一个程序中使用接口时,接口时多个模块间的耦合标准。从某种程度上来看,接口类似于整个系统的 总纲 ,它制定了系统各模块应该遵循的标准,因此一个系统中的接口不应该经常改变。一旦接口被改变,对整个系统升值其他系统的影响将是辐射性的,导致系统中大部分类都需要改写。
抽象类作为系统中多个子类的共同父类,它所体现的是一种模板设计。抽象类作为多个子类的抽象父类,可以被当成系统实现过程中的中间产品,这个中间产品已经实现了系统的部分功能(那些已经提供实现的方法),但这个产品依然不能被当成最终产品,必须有跟进异步的完善,这汇总完善可能有几种不同的方式。
在用法上,抽象类和接口也存在如下差别:
- 接口里只能包含抽象方法和默认方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法。
- 接口里不能定义静态方法,抽象类里可以定义静态方法。
- 接口里只能定义静态常量,不能定义普通成员变量;抽象类既可以定义普通成员变量,也可以定义静态常量。
- 接口i不包含构造器,抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
- 接口里不能包含初始化块,但抽象类则完全可以包含初始化块。
- 接口可以多实现,抽象类只能单继承。
什么是内部类?
将一个类放在另一个类的内部定义,这个定义在其他类内部的类被称为内部类,包含内部类的类也被称为外部类。
内部类的作用如下:
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
- 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问。但外部类不能访问内部类的实现细节,例如内部类的成员变量。
- 匿名内部类适合用于创建那些仅需要一次使用的类。
内部类与外部类还与如下区别:
- 内部类比外部类多使用三个修饰符,pivate ,protected,static 外部类不可以使用这三个修饰符。
- 非静态内部类不能拥有静态成员。
为什么静态内部的实例方法也不能访问外部类的实例属性?
因为静态内部类是外部类的类相关的,而不是外部类的对象相关的。也就说说,静态内部类对象不是寄生在外部类的实例中,而是寄生在外部类的本身中。当静态内部类对象存在时,并不存在一个被它寄生的外部类对象,静态内部类对象只持有外部类的类引用,没有持有外部类对象访问。如果允许静态内部类的实例方法访问外部类的实例成员,但找不到被寄生的外部类对象,这将引起错误。
Lambda表达式的使用
- 形参列表.形参列表允许省略形参列表。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。
- 箭头 (->)
- 代码块。如果代码块只包含一条语句,lambda 表达式允许省略代码块的花括号。如果Lambda 代码只有一条return语句,那么省略return 关键字。
遍历list 的方式
List<String> list=new ArrayList<>(); list.add("123"); list.add("123"); list.add("123"); list.add("123"); list.add("123"); list.forEach(System.out::println);
实现Runnable接口
new Thread(new Runnable() { @Override public void run() { System.out.println(123); } }).start(); new Thread(()-> System.out.println("123")).start();
使用匿名内部类
Runnable runnable=new Runnable() { @Override public void run() { System.out.println(123); } }; Runnable r1=()-> System.out.println("123"); r1.run();
如下写法
public class MyClass { public static void main(String[] args) { new MyClass().eat(()-> System.out.println("132")); new MyClass().fly(weather -> { System.out.println("今天"); }); new MyClass().test((a,b)->a+b); } public void eat(Eatable e){ System.out.println(e); e.taste(); } public void fly(Flyable f){ f.fly("Petterp"); } public void test(Addable a){ a.add(5,3); } } interface Eatable{ void taste(); } interface Flyable{ void fly(String weather); } interface Addable{ int add(int a,int b); }
什么是枚举?
枚举类是一种特殊的类,他一样可以有自己的成员变量,方法,可以实现一个或者多个接口,也可以定义自己的构造器。一个 Java源文件中最多只能定义一个 public 访问权限的枚举类,且该 Java 源文件也必须和枚举类的类名相同。
枚举类与普通类之间有如下区别:
- 枚举类可以实现一个或多个接口,使用 enum 定义的枚举类默认集成了 java.lang.Enum 类,而不是默认集成 Object 类,因此枚举类不能显示继承其他父类。其中java.lang.Enum类实现了 JAVA.langSerializable 和 java.lang.Comparable两个接口。
- 使用Enum 定义,非抽象的枚举类默认会使用 final 修饰,因此枚举类不能派生子类。
- 枚举类的构造器只能使用 private 访问控制符,如果省略了构造器的访问控制符,则默认使用 private修饰,如果强制指定访问控制符,则 只能 指定 private修饰符。
- 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不能产生实例。列出这些实例时,系统会自动添加 public static fianl 修饰。
枚举类的使用:
public enum SeasonEnum { a,b,c,d; public String name; } class Demo{ public static void main(String[] args) { for (SeasonEnum s:SeasonEnum.values()){ //打印枚举值的索引 System.out.println(s.ordinal()); } //如果该枚举对象位于指定枚举对象之后,则返回正整数; //如果该枚举对象位于指定枚举对象之前,则返回负整数,否则返回0 System.out.println(SeasonEnum.valueOf("a").compareTo(SeasonEnum.valueOf("b"))); //创建枚举类对象 SeasonEnum seasonEnum=Enum.valueOf(SeasonEnum.class,"a"); seasonEnum.name="Petterp"; System.out.println(seasonEnum.name); } }
枚举类也可以用于 switch
枚举类带构造器的使用
public enum SeasonEnum { //此处的枚举值必须调用对应的构造器来创建 MALE("男"), FEmale("女"); private final String name; //枚举类的构造器只能使用private修饰 SeasonEnum(String name) { this.name = name; } public String getName() { return name; } } class Demo { public static void main(String[] args) { System.out.println(SeasonEnum.FEmale); } }
以上代码如同于以下代码
public static final SeasonEnum MALE=new SeasonEnum("男"); public static final SeasonEnum FEmale=new SeasonEnum("女");
在枚举类中列出枚举值是,实际上就是调用构造器创建枚举类对象,只是这里无须使用new 关键字,也无需显示调用构造器。前面列出枚举值是无须传入参数,甚至无须使用括号,仅仅是因为前面的枚举类包含无参数的构造器。
实现接口的枚举类
枚举类也可以实现一个或多个接口,与普通类实现一个或多个接口完全一样,枚举类实现一个或多个接口时,也需要实现该接口所包含的方法。
public enum SeasonEnum implements Port { //此处的枚举值必须调用对应的构造器来创建 MALE("男") { @Override public void info() { System.out.println("这个枚举值代表男性"); } }, FEmale("女") { @Override public void info() { System.out.println("这个枚举值代表女性"); } }; private final String name; //枚举类的构造器只能使用private修饰 SeasonEnum(String name) { this.name = name; } public String getName() { return name; } } class Demo { public static void main(String[] args) { SeasonEnum.FEmale.info(); } }
上面的代码看起来有些奇怪,当创建 MALE 和 FEMALE 两个枚举值时,后面又紧跟了一对花括号,这队花括号包含了一个 info 方法定义。花括号部分实际上就是一个类体部分,在这种情况下,当创建 MALE,FEMALE 枚举值是,并不是直接创建 SeasonEnum枚举类的实例,而是相当于创建 SeasonEnum的匿名子类的实例。因为粗体字括号部分实际上是匿名内部类的类体部分,所以这个部分的代码语法与匿名内部类语法大致相似,只是它依然是枚举类的匿名内部类。
枚举类不是用 final 修饰吗,为什么还可以派生子类?
并不是所有的枚举类都是用了 final 修饰,非抽象的枚举类才默认使用 final 修饰,对于一个抽象的枚举类而言,只要它包含了抽象方法,它就是抽象枚举类,系统会默认是使用 abstart修饰。
枚举类可以包含抽象方法吗?
public enum Operation { Plus { @Override public double eval(double x, double y) { return 0; } }, MINUS{ public double eval(double x,double y){ return x-y; } }; //为枚举类定义一个抽象方法 //这个抽象方法由不同的枚举值提供不同的实现 public abstract double eval(double x,double y); public static void main(String[] args) { System.out.println(Operation.Plus.eval(3,4)); } //枚举类里定义抽象方法是不能使用 abstart 关键字将枚举类定义成抽象类(因为系统自动会为它添加 abstart 关键字),但因为枚举类需要显示创建枚举值而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现,否则将出现编译错误。