www.2527.com_澳门新葡8455手机版_新京葡娱乐场网址_
做最好的网站

面向对象下,Java基础知识复习

2019-10-06 04:11 来源:未知

static表示静态修饰符,使用static修饰的变量,在Java中分配内存后一直存在,直到程序退出才释放空间。用static修饰的变量可以直接使用类名加“.”的方式访问。

以下是《疯狂Java讲义》中的一些知识,如有错误,烦请指正。

本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-java.html

final表示不可改变的。如果修饰类,表示该类是一个最终类,不可被继承,默认其中的方法都为final方法。如果修饰方法,不能被子类覆盖,但是能被子类继承(父类中的private方法也是不能被子类覆盖的,默认也是final)。如果修饰成员变量,表示该变量是一个常量,只能被赋值一次,赋值之后不可以被改变。


本文收集整理了 Android 面试中会遇到与 Java 知识相关的简述题。

重载,表示同一个类中可以有多个方法名相同的方法,但是这些方法的参数列表各不相同(参数个数、类型或者顺序)。重写,表示子类对父类中的某个方法重新编写,相同的内容包括方法名、参数和返回类型。子类的方法访问修饰符范围要大于等于父类中的范围,子类只能比父类抛出更少的异常。声明为final的方法不能被重写。声明为static的方法不能被重写,但是能被再次声明。子类和父类在同一包下,子类能重写除了private和final的所有方法。子类和父类不在同一包下,子类只能重写父类声明为public和protected的方法。

Java8增强的包装类

自动装箱就是把一个基本类型的变量直接赋给对应的包装类变量,自动拆箱则与之相反。
把字符串类型转换成基本类型:

  • 除了Character之外的所有包装类都提供了一个parseXxx(String s)静态方法。
  • 利用包装类提供的Xxx(String s)构造器

String提供了多个重载的valueOf()方法,用于将基本类型转换成字符串。或者将基本类型与""进行连接运算,String intstr = 5 "";

public class Primitive2String
{
    public static void main(String[] args)
    {
        String intStr = "123";
        // 把一个特定字符串转换成int变量
        int it1 = Integer.parseInt(intStr);
        int it2 = new Integer(intStr);
        System.out.println(it2);
        String floatStr = "4.56";
        // 把一个特定字符串转换成float变量
        float ft1 = Float.parseFloat(floatStr);
        float ft2 = new Float(floatStr);
        System.out.println(ft2);
        // 把一个float变量转换成String变量
        String ftStr = String.valueOf(2.345f);
        System.out.println(ftStr);
        // 把一个double变量转换成String变量
        String dbStr = String.valueOf(3.344);
        System.out.println(dbStr);
        // 把一个boolean变量转换成String变量
        String boolStr = String.valueOf(true);
        System.out.println(boolStr.toUpperCase());
    }
}

注意:将-128-127之间的同一个整数自动装箱成Integer实例时,永远都是引用cache数组的同一个元素,所以他们全部相等;不在这个范围的整数自动装箱城Integer,只有两个包装类引用指向同一个对象时才相等。

Java 7为所有包装类增加一个新方法:compare(x,y)。该方法用于比较两个包装类实例,当x>y,返回大于0的数;当x==y,返回0;否则返回小于0的数。还有其他的一些方法不做介绍了。

面向对象

每个内部类都能独立的继承一个接口,与外围类无关。成员内部类,作为外部类的成员,可以使用外部类的所有成员和方法,但是外部类要先拥有成员内部类的实例对象,才可以访问内部类。成员内部类不能有任何static的变量或方法。只有先创建外部类,才能在创建成员内部类。(new 外部类实例;外部类名.内部类名 内部类对象名 = 外部类实例.new 内部类类名)。局部内部类,是定义在一个方法或者一个作用域里面的类,它的访问权限仅限于该方法或者该作用域内。匿名内部类,没有访问修饰符;new 匿名内部类;这里举个简单例子。

处理对象

打印对象和toString方法:toString方法是系统将会输出该对象的“自我描述”信息,用以告诉外界对象具有的状态信息。
Object 类提供的toString方法总是返回该对象实现类的类名 @ hashCode值。这个并不能真正实现自我描述的功能,因此用户必须在自定义类中重写Object类的toString方法。通常可返回类名[filed1=值1,field2=值2,...]

==和equals比较运算符
==要求两个引用变量指向同一个对象才会返回true,不可用于比较类型上没有父子关系的两个对象;对于基本类型变量,只要变量值相等返回true。equals方法则允许用户提供自定义的相等规则。Object类提供的equals方法判断两个对象相等的标准与==完全相同。因此开发者通常需要重写equals方法。

字符串直接量与字符串对象
Java程序直接使用"hello"的字符串直接量时,JVM将会使用常量池管理这些字符串;当使用new String("hello"),JVM会先使用常量池管理"hello"的字符串直接量,在调用String类的构造器创建新的String对象,新创建得到String对象被保存在堆内存中。

public class StringCompareTest
{
    public static void main(String[] args)
    {
        // s1直接引用常量池中的"疯狂Java"
        String s1 = "疯狂Java";
        String s2 = "疯狂";
        String s3 = "Java";
        // s4后面的字符串值可以在编译时就确定下来
        // s4直接引用常量池中的"疯狂Java"
        String s4 = "疯狂"   "Java";
        // s5后面的字符串值可以在编译时就确定下来
        // s5直接引用常量池中的"疯狂Java"
        String s5 = "疯"   "狂"   "Java";
        // s6后面的字符串值不能在编译时就确定下来,
        // 不能引用常量池中的字符串
        String s6 = s2   s3;
        // 使用new调用构造器将会创建一个新的String对象,
        // s7引用堆内存中新创建的String对象
        String s7 = new String("疯狂Java");
        System.out.println(s1 == s4); // 输出true
        System.out.println(s1 == s5); // 输出true
        System.out.println(s1 == s6); // 输出false
        System.out.println(s1 == s7); // 输出false
    }
}

equals方法重写

class Person
{
    private String name;
    private String idStr;
    public Person(){}
    public Person(String name , String idStr)
    {
        this.name = name;
        this.idStr = idStr;
    }
    // 此处省略name和idStr的setter和getter方法。
    // name的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }

    // idStr的setter和getter方法
    public void setIdStr(String idStr)
    {
        this.idStr = idStr;
    }
    public String getIdStr()
    {
        return this.idStr;
    }
    // 重写equals()方法,提供自定义的相等标准
    public boolean equals(Object obj)
    {
        // 如果两个对象为同一个对象
        if (this == obj)
            return true;
        // 只有当obj是Person对象
        if (obj != null && obj.getClass() == Person.class)
        {
            Person personObj = (Person)obj;
            // 并且当前对象的idStr与obj对象的idStr相等才可判断两个对象相等
            if (this.getIdStr().equals(personObj.getIdStr()))
            {
                return true;
            }
        }
        return false;
    }
}
public class OverrideEqualsRight
{
    public static void main(String[] args)
    {
        Person p1 = new Person("孙悟空" , "12343433433");
        Person p2 = new Person("孙行者" , "12343433433");
        Person p3 = new Person("孙悟饭" , "99933433");
        // p1和p2的idStr相等,所以输出true
        System.out.println("p1和p2是否相等?"
              p1.equals(p2));
        // p2和p3的idStr不相等,所以输出false
        System.out.println("p2和p3是否相等?"
              p2.equals(p3));
    }
}

注意instanceof当前面对象是后面类的实例或者子类的实例时都将返回true,在重写equals方法里不适用。

Java面向对象的三个特征与含义

  • 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。
  • 封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口(可以想想普通洗衣机和全自动洗衣机的差别,明显全自动洗衣机封装更好因此操作起来更简单;我们现在使用的智能手机也是封装得足够好的,因为几个按键就搞定了所有的事情)。
  • 多态:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的(就像电动剃须刀是A系统,它的供电系统是B系统,B系统可以使用电池供电或者用交流电,甚至还有可能是太阳能,A系统只会通过B类对象调用供电的方法,但并不知道供电系统的底层实现是什么,究竟通过何种方式获得了动力)。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2. 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
bt.setOnClickListener(new OnClickListener() { @Override public void onClick { // TODO Auto-generated method stub } });

类成员

即使通过null对象来访问类成员,程序也不会引发NullPointerException。
静态初始化块也是类成员的一种。

单例类(Singleton)
如果一个类始终只能创建一个对象,称为单例类。
条件:

  1. 我们把该类的构造器使用Private修饰,从而把该 类的所有构造器隐藏起来。
  2. 则需要提供一个public方法作为该类的访问点,用于创建该类的对象,且必须使用static修饰
  3. 该类还必须缓存已经创建的对象,必须用static修饰
class Singleton
{
    // 使用一个类变量来缓存曾经创建的实例
    private static Singleton instance;
    // 将构造器使用private修饰,隐藏该构造器
    private Singleton(){}
    // 提供一个静态方法,用于返回Singleton实例
    // 该方法可以加入自定义的控制,保证只产生一个Singleton对象
    public static Singleton getInstance()
    {
        // 如果instance为null,表明还不曾创建Singleton对象
        // 如果instance不为null,则表明已经创建了Singleton对象,
        // 将不会重新创建新的实例
        if (instance == null)
        {
            // 创建一个Singleton对象,并将其缓存起来
            instance = new Singleton();
        }
        return instance;
    }
}
public class SingletonTest
{
    public static void main(String[] args)
    {
        // 创建Singleton对象不能通过构造器,
        // 只能通过getInstance方法来得到实例
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2); // 将输出true
    }
}

Java 多态

什么是多态

面向对象的三大特性:封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。

多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)

实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实 际类型,根据其实际的类型调用其相应的方法。

多态的作用:消除类型之间的耦合关系。

现实中,关于多态的例子不胜枚举。比方说按下 F1 键这个动作,如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;如果当前在 Word 下弹出的就是 Word 帮助;在 Windows 下弹出的就是 Windows 帮助和支持。同一个事件发生在不同的对象上会产生不同的结果。 下面是多态存在的三个必要条件,要求大家做梦时都能背出来!

多态存在的三个必要条件 一、要有继承; 二、要有重写; 三、父类引用指向子类对象。

多态的好处:

  1. 可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
  2. 可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
  3. 接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3 所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
  4. 灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
  5. 简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。

Java中多态的实现方式:接口实现,继承父类进行方法重写,同一个类中进行方法重载。

再看一个例子

final修饰符

用于表示修饰的类、方法、变量不可变。final修饰的成员变量必须由程序员显式地指定初始值。系统不会对final成员进行隐式初始化。
类变量:必须在静态初始化块或声明该变量时指定初始值
实例变量:必须在非静态初始化块或声明该变量时或构造器中指定初始值。
使用final修饰局部变量时既可以在定义时指定默认值,也可以不指定默认值。

final修饰基本类型和引用变量的区别
当使用final修饰基本数据类型变时,不能对其重新赋值,不能被改变。但对引用类型的变量而言,它仅仅保存的是一个引用,final只能保证他的地址不变,但不能保证对象,所以引用类型完全可以改变他的对象。

class Person
{
    private int age;
    public Person(){}
    // 有参数的构造器
    public Person(int age)
    {
        this.age = age;
    }
    // 省略age的setter和getter方法
    // age的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
        return this.age;
    }
}
public class FinalReferenceTest
{
    public static void main(String[] args)
    {
        // final修饰数组变量,iArr是一个引用变量
        final int[] iArr = {5, 6, 12, 9};
        System.out.println(Arrays.toString(iArr));
        // 对数组元素进行排序,合法
        Arrays.sort(iArr);
        System.out.println(Arrays.toString(iArr));
        // 对数组元素赋值,合法
        iArr[2] = -8;
        System.out.println(Arrays.toString(iArr));
        // 下面语句对iArr重新赋值,非法
        // iArr = null;
        // final修饰Person变量,p是一个引用变量
        final Person p = new Person(45);
        // 改变Person对象的age实例变量,合法
        p.setAge(23);
        System.out.println(p.getAge());
        // 下面语句对p重新赋值,非法
        // p = null;
    }
}

可执行“宏替换”的final变量
对一个final变量来说,不管它是类变量、实例变量,还是局部变量,只要该变量满足3个条件,这个final变量就不再是一个变量,而是相当于一个直接量。

  • 使用final修饰符修饰;
  • 在定义该final变量时指定了初始值;
  • 该初始值可以在编译时就被确定下来。
public class StringJoinTest
{
    public static void main(String[] args)
    {
        String s1 = "疯狂Java";
        // s2变量引用的字符串可以编译时就确定出来,
        // 因此s2直接引用常量池中已有的"疯狂Java"字符串
        String s2 = "疯狂"   "Java";
        System.out.println(s1 == s2);
        // 定义2个字符串直接量
        String str1 = "疯狂";     //①
        String str2 = "Java";     //②
        // 将str1和str2进行连接运算
        String s3 = str1   str2;
        System.out.println(s1 == s3);//false
    }
}

str1和str2只是普通变量,编译器不会执行宏替换;只要定义时添加final修饰,结果为true。注意对于final实例变量,只有定义该变量时指定初始值才会有宏变量的效果。

final方法
final 修饰的方法不可以被重写。
final 修饰的方法仅仅是不能重写,但它完全可以被重载。

public class PrivateFinalMethodTest
{
    private final void test(){}
}
class Sub extends PrivateFinalMethodTest
{
    // 下面方法定义将不会出现问题
    public void test(){}
}

final类
final 修饰的类不可以被继承

语法知识

public class Test { public static void main(String[] args) { } public void test(final int b) { final int a = 10; new Thread(){ public void run() { System.out.println; System.out.println; }; }.start(); }}

抽象类

抽象方法和类都必须使用abstract来修饰,含有抽象方法的类一定为抽象类,抽象类里也可以没有抽象方法。
抽象类不能被实例化,可以通过其子类给他赋值;普通类里有的抽象里也有。
定义抽象方法只需在普通方法上增加abstract修饰符,并把普通方法的方法体(也就是方法后花括号括起来的部分)全部去掉,并在方法后增加分号即可。
注意:static和abstract不能同时修饰某个方法,即没有所谓的类抽象方法,即使有调用一个没有方法体的方法也会引起错误。

抽象类的作用
抽象类代表了一种未完成的类设计,它体现的是一种模板。

& 和 && 的区别

&和&&都是可以作为逻辑运算符的,其逻辑运算规则是相同的。

但&作为逻辑运算符时,即使第一个操作符是false,那么它仍然会计算第二个操作符。&&短路与,如果第一个操作符为false,那么它不会再去计算第二个操作符。

这里test方法的参数为final修饰,在其中还定义了一个final的变量,然后在匿名内部类中进行使用。在run方法中,对于a,java编译器能确定其值,所以在编译的时候会直接在匿名类的常量池中添加一个值相等的变量;对于b,参数的值无法在编译的时候确定,这时会将test方法中的形参a以参数的形式传进来对匿名内部类中的拷贝进行初始化赋值。这里就有一个问题,内部类中的变量和test中的局部变量不是一个对象,所以java直接限制为final变量,不允许改变,这样数据不一致的问题就得到了解决。静态内部类,使用static修饰的内部类。内部类不依赖外部类,并且不能使用外部类非static的成员变量或方法,创建静态内部类对象,外部类名.内部类名 内部类对象名 = new 外部类名.内部类类名()。

接口

接口定义的是多个类共同的行为规范,这些行为是与外部交流的通道,这就意味着接口里通常是定义一组公用的方法。
接口里不能包含普通方法,所有方法都是抽象方法,Java8允许定义默认方法。

接口定义

[修饰符] interface 接口名 extends 父接口1,父接口2 
{
    零个到多个常量定义...
    零个到多个抽象方法定义...
    零个到多个内部类、接口、枚举定义...
    零个到多个默认方法或类方法定义...
}

修饰符可以是public或者省略。
常量都是:public static final修饰
实例方法都是:public abstract 修饰
类方法用public static修饰
默认方法用public default修饰
内部的类:public static
接口里面没有构造器和初始化块。
类方法可以使用接口直接调用,默认方法要通过使用接口的实例来调用。

接口继承
接口的继承和类继承不一样,接口完全支持多继承,子接口扩展某个父接口将会获得父接口的所有抽像方法,类变量。

使用接口
一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法);
否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类

接口和抽象类的相似性

  • 接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承。
  • 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。

接口与抽象类的区别

  • 接口里只能包含抽象方法,不同包含已经提供实现的方法;抽象类则完全可以包含普通方法。
  • 接口里不能定义静态方法;抽象类里可以定义静态方法。
  • 接口里只能定义静态常量属性,不能定义普通属性;抽象类里则既可以定义普通属性,也可以定义静态常量属性。
  • 接口不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而让其子类调用这些构造器来完成属于抽象类的初始化操作。
  • 接口里不能包含初始化块,但抽象类则完全可以包含初始化块。
  • 一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足。

用最有效率的方法算出2乘以8等于几?

2 << 3

定义,type arrayName[][]或者type[][] arrayName。静态初始化,int array[][] = {{1,2},{1,2,3},{1,2,3,4}};在Java中,把二维数组看作是数组的数组,数组的空间是不连续的,所以不要求二维数组每一维大小相等。动态初始化,可以直接给数组的每一维赋值同样的大小,也可以先确定数组最高维的大小,然后给每一维具体分配。int array[][] = new int[2][3];int array[][] = new int[2][],array[0] = new int[5],array[1]=new int[10]。

内部类

我们把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类,有的也叫嵌套类,包含内部类的类也被称为外部类有的也叫宿主类。内部类提供了更好的封装,内部类成员可以直接访问外部类的私有数据,因为内部类被当成其他外部类成员。匿名内部类适合用于创建那些仅需要一次使用的类。
区别:

  • 可以多使用修饰符private、protected、static
  • 非静态内部类不能拥有静态成员

非静态内部类
非静态内部类中可以访问外部类的private成员,是因为在非静态内部类中保存了外部类对象的引用。

public class DiscernVariable
{
    private String prop = "外部类的实例变量";
    private class InClass
    {
        private String prop = "内部类的实例变量";
        public void info()
        {
            String prop = "局部变量";
            // 通过 外部类类名.this.varName 访问外部类实例变量
            System.out.println("外部类的实例变量值:"
                  DiscernVariable.this.prop);
            // 通过 this.varName 访问内部类实例的变量
            System.out.println("内部类的实例变量值:"   this.prop);
            // 直接访问局部变量
            System.out.println("局部变量的值:"   prop);
        }
    }
    public void test()
    {
        InClass in = new InClass();
        in.info();
    }
    public static void main(String[] args)
    {
        new DiscernVariable().test();
    }
}

非静态内部类的成员只在内部类范围是可知的,并不能直接被外部类调用,如果外部类需要访问,则必须显示创建非静态内部类对象来调用其实例成员。非静态内部类不可以定义静态成员。

静态内部类
如果用static修饰一个内部类,称为静态内部类。
静态内部类可以包含静态成员,也可以包含非静态成员。所以静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。
静态内部类的对象寄存在外部类里,非静态内部类的对象寄存在外部类实例里
外部类依然不能直接访问静态内部类的成员,但可以使用静态内部类的类名访问静态内部类的类成员或者静态内部类对象作为调用者访问静态内部类的实例成员。

使用内部类

  1. 在外部类内部使用内部类-不要在外部类的静态成员中使用非静态内部类,因为静态成员不能访问非静态成员。
  2. 在外部类以外使用非静态内部类。
  • private 修饰的内部类只能在外部类内部使用。
  • 在外部类以外的地方使用内部类,内部类完整的类名应该OuterClass.InnerClass.
  • 在外部类以外的地方使用非静态内部类创建对象的语法如下:OuterInstance.new InnerConstructor(),创建非静态内部类实例以来于外部类实例。
class Out
{
    // 定义一个内部类,不使用访问控制符,
    // 即只有同一个包中其他类可访问该内部类
    class In
    {
        public In(String msg)
        {
            System.out.println(msg);
        }
    }
}
public class CreateInnerInstance
{
    public static void main(String[] args)
    {
        Out.In in = new Out().new In("测试信息");
        /*
        上面代码可改为如下三行代码:
        使用OutterClass.InnerClass的形式定义内部类变量
        Out.In in;
        创建外部类实例,非静态内部类实例将寄存在该实例中
        Out out = new Out();
        通过外部类实例和new来调用内部类构造器创建非静态内部类实例
        in = out.new In("测试信息");
        */
    }
}

创建静态内部类的子类时,必须存在一个外部类对象,然后才能调用非静态内部类的构造器。

public class SubClass extends Out.In
{
    //显示定义SubClass的构造器
    public SubClass(Out out)
    {
        //通过传入的Out对象显式调用In的构造器
        out.super("hello");
    }
}

注意:非静态内部类的子类不一定是内部类,可以是外部类。但其实例必须保留一个引用,指向父类所在外部类的对象。

  1. 在外部类以外使用静态内部类
    在外部类以外的地方使用静态内部类创建对象的语法如下:new OuterClass.InnerConstructer();
    使用静态内部类相对容易,只要把外部类当成静态内部类的包空间即可。所以,一般优先考虑静态内部类。

注意:子类的内部类不可能重写父类的内部类,因为即使内部类类名相同,外部类空间不同,就不可能完全同名。

局部内部类
如果把一个内部类放在方法里定义,这就是局部内部类,仅仅在这个方法里有效。局部内部类不能在外部类以外的地方使用,那么局部内部类也不能使用访部控制符和static修饰。
局部内部类使用很有限。

匿名内部类
匿名内部类适合创建那种只需要一次使用的类,定义匿名内部类的语法格式如下:

new 父类构造器(实例列表) |实现接口)
{
      //匿名内部类的 类体部分
}

必须继承一个父类或者实现一个接口,但最多继承一个父类或者实现一个接口。
两个限制:匿名内部类不能是抽象类;匿名内部类不能定义构造器。

interface Product
{
    public double getPrice();
    public String getName();
}
public class AnonymousTest
{
    public void test(Product p)
    {
        System.out.println("购买了一个"   p.getName()
              ",花掉了"   p.getPrice());
    }
    public static void main(String[] args)
    {
        AnonymousTest ta = new AnonymousTest();
        // 调用test()方法时,需要传入一个Product参数,
        // 此处传入其匿名实现类的实例
        ta.test(new Product()
        {
            public double getPrice()
            {
                return 567.8;
            }
            public String getName()
            {
                return "AGP显卡";
            }
        });
    }
}

如果局部变量被匿名内部类访问,该变量相当于自动使用final修饰。也就是effective final:对于内部类访问的局部变量可以用final修饰,也可以不用,但是一次赋值后不能再次赋值。

interface A
{
    void test();
}
public class ATest
{
    public static void main(String[] args)
    {
        int age = 8;     // ①
        // 下面代码将会导致编译错误
        // 由于age局部变量被匿名内部类访问了,因此age相当于被final修饰了
        age = 2;
        A a = new A()
        {
            public void test()
            {
                // 在Java 8以前下面语句将提示错误:age必须使用final修饰
                // 从Java 8开始,匿名内部类、局部内部类允许访问非final的局部变量
                System.out.println(age);
            }
        };
        a.test();
    }
}

Java 中的 SoftReference 是什么

Java 中的 SoftReference 即对象的软引用。如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。使用软引用能防止内存泄露,增强程序的健壮性。

SoftReference的特点是它的一个实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null

用Map集合缓存软引用的Bitmap对象

Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
//强引用的Bitmap对象
Bitmap bitmap = BitmapFactory.decodeStream(InputStream);
//软引用的Bitmap对象
SoftReference<Bitmap> bitmapcache = new SoftReference<Bitmap>(bitmap);
//添加该对象到Map中使其缓存
imageCache.put("1",softRbitmap);

// ...

//从缓存中取软引用的Bitmap对象
SoftReference<Bitmap> bitmapcache_ = imageCache.get("1");
//取出Bitmap对象,如果由于内存不足Bitmap被回收,将取得空
Bitmap bitmap_ = bitmapcache_.get();

抽象类可以含有具体的方法;子类使用extends关键字来继承抽象类,如果子类不是抽象类,它需要实现父类中所有的抽象方法,或者继续声明为抽象类;抽象类中也可以不含抽象方法;抽象类中可以有成员变量;抽象类可以有构造函数,只是不能直接用来创建抽象类的实例,在继承了抽象类的子类中可以通过super调用抽象类中的构造函数;抽象类可以继承一个类实现多个接口。接口接口中可以且只能有 public static final修饰的常量。子类需要使用implements实现接口,需要实现接口中声明的所有方法;接口没有构造函数;接口默认修饰符是public,不能改为其他修饰符。

Lambda表达式

                                                                                  Lambda表达式主要作用就是代替匿名内部类的繁琐语法。                                    它由三部分组成:
  • 形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。
  • 箭头(->)。
  • 代码块。如果代码块只有包含一条语句,Lambda表达式允许省略代码块的花括号,如果省略了代码块的花括号,这条语句不要用花括号表示语句结束。Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而它的代码块中仅有一条省略了return的语句,Lambda表达式会自动返回这条语句的值
interface Eatable
{
    void taste();
}
interface Flyable
{
    void fly(String weather);
}
interface Addable
{
    int add(int a , int b);
}
public class LambdaQs
{
    // 调用该方法需要Eatable对象
    public void eat(Eatable e)
    {
        System.out.println(e);
        e.taste();
    }
    // 调用该方法需要Flyable对象
    public void drive(Flyable f)
    {
        System.out.println("我正在驾驶:"   f);
        f.fly("【碧空如洗的晴日】");
    }
    // 调用该方法需要Addable对象
    public void test(Addable add)
    {
        System.out.println("5与3的和为:"   add.add(5, 3));
    }
    public static void main(String[] args)
    {
        LambdaQs lq = new LambdaQs();
        // Lambda表达式的代码块只有一条语句,可以省略花括号。
        lq.eat(()-> System.out.println("苹果的味道不错!"));
        // Lambda表达式的形参列表只有一个形参,省略圆括号
        lq.drive(weather ->
        {
            System.out.println("今天天气是:"   weather);
            System.out.println("直升机飞行平稳");
        });
        // Lambda表达式的代码块只有一条语句,省略花括号
        // 代码块中只有一条语句,即使该表达式需要返回值,也可以省略return关键字。
        lq.test((a , b)->a   b);
    }
}

Lambda表达式与函数式接口
如果采用匿名内部类语法来创建函数式接口的实例,只要实现一个抽象方法即可,在这种情况下即可采用Lambda表达式来创建对象,该表达式创建出来的对象的目标类型就是这个函数式接口。

Lambda表达式有如下两个限制:

  • Lambda表达式的目标类型必须是明确的函数式接口。
  • Lambda表达式只能为函数式接口创建对象。Lambda表达式只能实现一个方法,因此它只能为只有一个抽象方法的接口(函数式接口)创建对象。

为了保证Lambda表达式的目标类型是一个明确的函数式接口,可以有如下三种常见方式:

  • 将Lambda表达式赋值给函数式接口类型的变量。
  • 将Lambda表达式作为函数式接口类型的参数传给某个方法。
  • 使用函数式接口对Lambda表达式进行强制类型转换。

方法引用与构造器引用

如果Lambda表达式的代码块只有一条代码,可以省略表达式中代码块的花括号,还可以在代码块中使用方法引用和构造器引用。

种类 示例 说明 Lambda表达式
引用类方法 类名::类方法 函数式接口中被实现方法的全部参数传给该类方法作为参数。 (a,b,...) -> 类名.类方法(a,b, ...)
引用特定对象的实例方法 特定对象::实例方法 函数式接口中被实现方法的全部参数传给该实例方法作为参数。 (a,b, ...) -> 特定对象.实例方法(a,b, ...)
引用某类对象的实例方法 类名::实例方法 函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数。 (a,b, ...) ->a.实例方法(b, ...)
引用构造器 类名::new 函数式接口中被实现方法的全部参数传给该构造器作为参数 (a,b, ...) ->new 类的构造器(a,b, ...)
import javax.swing.*;
@FunctionalInterface
interface Converter{
    Integer convert(String from);
}
@FunctionalInterface
interface MyTest
{
    String test(String a , int b , int c);
}
@FunctionalInterface
interface YourTest
{
    JFrame win(String title);
}
public class MethodRefer
{
    public static void main(String[] args)
    {
        // 下面代码使用Lambda表达式创建Converter对象
//      Converter converter1 = from -> Integer.valueOf(from);
//      // 方法引用代替Lambda表达式:引用类方法。
//      // 函数式接口中被实现方法的全部参数传给该类方法作为参数。
//      Converter converter1 = Integer::valueOf;
//      Integer val = converter1.convert("99");
//      System.out.println(val); // 输出整数99



        // 下面代码使用Lambda表达式创建Converter对象
//      Converter converter2 = from -> "fkit.org".indexOf(from);
//      // 方法引用代替Lambda表达式:引用特定对象的实例方法。
//      // 函数式接口中被实现方法的全部参数传给该方法作为参数。
//      Converter converter2 = "fkit.org"::indexOf;
//      Integer value = converter2.convert("it");
//      System.out.println(value); // 输出2



        // 下面代码使用Lambda表达式创建MyTest对象
//      MyTest mt = (a , b , c) -> a.substring(b , c);
        // 方法引用代替Lambda表达式:引用某类对象的实例方法。
        // 函数式接口中被实现方法的第一个参数作为调用者,
        // 后面的参数全部传给该方法作为参数。
//      MyTest mt = String::substring;
//      String str = mt.test("Java I Love you" , 2 , 9);
//      System.out.println(str); // 输出:va I Lo



        // 下面代码使用Lambda表达式创建YourTest对象
//      YourTest yt = (String a) -> new JFrame(a);
        // 构造器引用代替Lambda表达式。
        // 函数式接口中被实现方法的全部参数传给该构造器作为参数。
        YourTest yt = JFrame::new;
        JFrame jf = yt.win("我的窗口");
        System.out.println(jf);
    }
}

Lambda表达式与匿名内部类
相同点:

  • Lambda表达式与匿名内部类一样,都可以直接访问“effectively final”的局部变量,以及外部类的成员变量(包括实例变量和类变量)。
  • Lambda表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口继承得到的默认方法。

区别:

  • 匿名内部类可以为任意接口创建实例——不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可。但Lambda表达式只能为函数式接口创建实例。
  • 匿名内部类可以为抽象类、甚至普通类创建实例,但Lambda表达式只能为函数式接口创建实例。
  • 匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lambda表达式的代码块不允许调用接口中定义的默认方法。

使用Lambda表达式调用Arrays的类方法
Arrays类的有些方法需要Comparator、XxxOperator、XxxFuncton等接口实例,这些接口都是函数式接口,因此可以使用Lambda表达式调用Arrays方法。

import java.util.Arrays;
public class LambdaArrays
{
    public static void main(String[] args)
    {
        String[] arr1 = new String[]{"java" , "fkava" , "fkit", "ios" , "android"};
        Arrays.parallelSort(arr1, (o1, o2) -> o1.length() - o2.length());
        System.out.println(Arrays.toString(arr1));
        int[] arr2 = new int[]{3, -4 , 25, 16, 30, 18};
        // left代表数组中前一个所索引处的元素,计算第一个元素时,left为1
        // right代表数组中当前索引处的元素
        Arrays.parallelPrefix(arr2, (left, right)-> left * right);
        System.out.println(Arrays.toString(arr2));
        long[] arr3 = new long[5];
        // operand代表正在计算的元素索引
        Arrays.parallelSetAll(arr3 , operand -> operand * 5);
        System.out.println(Arrays.toString(arr3));
    }
}

谈一下对 Java 中的 abstract 的理解

abstract(抽象)可以修饰类、方法

如果将一个类设置为abstract,则此类必须被继承使用。此类不可生成对象,必须被继承使用。 abstract可以将子类的共性最大限度的抽取出来,放在父类中,以提高程序的简洁性。 abstract虽然不能生成对象,但是可以声明,作为编译时类型,但不能作为运行时类型。 final和abstract永远不会同时出现。

当abstract用于修饰方法时,此时该方法为抽象方法,此时方法不需要实现,实现留给子类覆盖,子类覆盖该方法之后方法才能够生效。

注意比较:
private void print(){};此语句表示方法的空实现。
abstract void print(); 此语句表示方法的抽象,无实现。

如果一个类中有一个抽象方法,那么这个类一定为一个抽象类。 反之,如果一个类为抽象类,那么其中可能有非抽象的方法。

如果让一个非抽象类继承一个含抽象方法的抽象类,则编译时会发生错误。因为当一个非抽象类继承一个抽象方法的时候,本着只有一个类中有一个抽象方法,那么这个类必须为抽象类的原则。这个类必须为抽象类,这与此类为非抽象冲突,所以报错。

所以子类的方法必须覆盖父类的抽象方法。方法才能够起作用。

为了实现多态,那么父类必须有定义。而父类并不实现,留给子类去实现。此时可将父类定义成abstract类。如果没有定义抽象的父类,那么编译会出现错误。

abstract 和 static 不能放在一起,否则便会出现错误。(这是因为static不可被覆盖,而abstract为了生效必须被覆盖。)

abstract 和 final 不能放在一起,否则便会出现错误。

Java反射机制是在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象,都能调用他的任意方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

枚举类

实例有限而且固定的类被称为枚举类
枚举类是一种特殊的类,它一样可以有自己的方法和属性,可以实现一个或者多个接口,也可以定义自己的构造器。一个Java源文件中最多只能定义一个public访问权限的枚举类,且该Java源文件也必须和该枚举类的类名相同。

与普通类的区别

  • 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是继承Object类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang. Comparable两个接口。
  • 枚举类的构造器只能使用private访问控制符,如果省略了其构造器的访问控制符,则默认使用private修饰;如果强制指定访问控制符,则只能指定private修饰符。
  • 枚举类的所有实例必须在枚举类中显式列出,否则这个枚举类将永远都不能产生实例。列出这些实例时系统会自动添加public static final修饰,无需程序员显式添加。
  • 所有枚举类都提供了一个values方法,该方法可以很方便地遍历所有的枚举值。
public enum SeasonEnum
{
    // 在第一行列出4个枚举实例
    SPRING,SUMMER,FALL,WINTER;
}

public class EnumTest
{
    public void judge(SeasonEnum s)
    {
        // switch语句里的表达式可以是枚举值
        switch (s)
        {
            case SPRING:
                System.out.println("春暖花开,正好踏青");
                break;
            case SUMMER:
                System.out.println("夏日炎炎,适合游泳");
                break;
            case FALL:
                System.out.println("秋高气爽,进补及时");
                break;
            case WINTER:
                System.out.println("冬日雪飘,围炉赏雪");
                break;
        }
    }
    public static void main(String[] args)
    {
        // 枚举类默认有一个values方法,返回该枚举类的所有实例
        for (SeasonEnum s : SeasonEnum.values())
        {
            System.out.println(s);
        }
        // 使用枚举实例时,可通过EnumClass.variable形式来访问
        new EnumTest().judge(SeasonEnum.SPRING);
    }
}

枚举类的属性、方法和构造器
枚举类也是一种类,只是它是一种比较特殊的类,因此它一样可以使用属性和方法。
枚举类通常应该设计成不可变类,也就说它的属性值不应该允许改变,这样会更安全,而且代码更加简洁。为此,我们应该将枚举类的属性都使用private final修饰。
一旦为枚举类显式定义了带参数的构造器,则列出枚举值时也必须对应地传入参数。

public enum Gender
{
    // 此处的枚举值必须调用对应构造器来创建
    MALE("男"),FEMALE("女");
    private final String name;
    // 枚举类的构造器只能使用private修饰
    private Gender(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }
}

实现接口的枚举类
枚举类也可以实现一个或多个接口。与普通类实现一个或多个接口完全一样,枚举类实现一个或多个接口时,也需要实现该接口所包含的方法。
如果需要每个枚举值在调用同一个方法时呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法,每个枚举值提供不同的实现方式,从而让不同枚举值调用同一个方法时具有不同的行为方式。

public enum Gender implements GenderDesc{
    //调用构造器创建枚举值
    MALE("男")
    //下面是类体
    {
        public void info(){
            System.out.println("枚举值代表男性");
        }
    },  
    FEMALE("女"){
        public void info(){
            System.out.println("枚举值代表男性");
        }
    };

    private final String name;
    private Gender(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }

}

创建MALE、FEMALE枚举值时不是直接创建Gender实例,而是创建Gender匿名子类的实例,。注意并不是所有的枚举类都是用了final修饰,非抽象的枚举类才默认使用final修饰。

包含抽象方法的枚举类
可以在枚举类里定义一个抽象方法,然后把这个抽象方法交给各枚举值去实现即可。
枚举类里定义抽象方法时无需显式使用abstract关键字将枚举类定义成抽象类,但因为枚举类需要显式创建枚举值,而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现,否则将出现编译错误。

public enum Operation
{
    PLUS
    {
        public double eval(double x , double y)
        {
            return x   y;
        }
    },
    MINUS
    {
        public double eval(double x , double y)
        {
            return x - y;
        }
    },
    TIMES
    {
        public double eval(double x , double y)
        {
            return x * y;
        }
    },
    DIVIDE
    {
        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));
        System.out.println(Operation.MINUS.eval(5, 4));
        System.out.println(Operation.TIMES.eval(5, 4));
        System.out.println(Operation.DIVIDE.eval(5, 4));
    }
}

Overload 和 Override

方法的重写(Overriding)和重载(Overloading)是Java多态性的不同表现。重写(Overriding)是父类与子类之间多态性的一种表现,而重载(Overloading)是一个类中多态性的一种表现。

如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被"屏蔽"了。

如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型或有不同的参数次序,则称为方法的重载(Overloading)。不能通过访问权限、返回类型、抛出的异常进行重载.

方法重载规则

  • 被重载的方法必须改变参数列表(参数个数或类型或顺序不一样);
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。

方法的重写规则

  • 参数列表必须完全与被重写方法的相同;
  • 返回类型必须完全与被重写方法的返回类型相同;
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
  • 父类的成员方法只能被它的子类重写。
  • 声明为final的方法不能被重写。
  • 声明为static的方法不能被重写,但是能够被再次声明。
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
  • 构造方法不能被重写。
  • 如果不能继承一个方法,则不能重写这个方法。

通过反射获得类的包名和类名等类的相关信息。

Overload 和 Override 的区别

区别点 方法重载 方法重写
参数列表 必须修改 一定不能修改
返回类型 可以修改 一定不能修改
异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问 可以修改 一定不能做更严格的限制(可以降低限制)
Test test = new Test();//获得包名test.getClass().getPackage().getName();//获得类名test. getName();

访问控制符 public、protected、�default、private区别

Java访问控制符的含义和使用情况:

修饰符 当前类 同一个包 子类 其他包
public
protected ×
default × ×
private × × ×

区别:

  1. public:可以被所有其他类所访问。
  2. protected:自身,子类及同一个包中类可以访问。
  3. default(默认):同一包中的类可以访问,声明时没有加修饰符,认为是friendly。
  4. private:只能被自己访问和修改。

接口:接口里的变量都隐式声明为public static final,而接口里的方法默认情况下访问权限为public。

通过Class类获得Test类的实例对象

Java 里的常量是怎么定义的

  1. 接口(Interface)的中变量默认为static final。
  2. Java 5.0中引入的Enum类型。
  3. 在普通类中使用static final修饰变量。
 //定义一个类型未知的Class类,Class<?> class = null;//方法1class = Class.forName("com.king.test");//方法2class = Test.class;

java中abstract,interface,final,static的总结

抽象类:abstract

  1. 只要有一个或一个以上抽象方法的类,必须用abstract声明为抽象类;
  2. 抽象类中可以有具体的实现方法;
  3. 抽象类中可以没有抽象方法;
  4. 抽象类中的抽象方法必须被它的子类实现,如果子类没有实现,则该子类继续为抽象类
  5. 抽象类不能被实例化,但可以由抽象父类指向的子类实例来调用抽象父类中的具体实现方法;通常作为一种默认行为;
  6. 要使用抽象类中的方法,必须有一个子类继承于这个抽象类,并实现抽象类中的抽象方法,通过子类的实例去调用;

接口:interface

  1. 接口中可以有成员变量,且接口中的成员变量必须定义初始化;
  2. 接口中的成员方法只能是方法原型,不能有方法主体;
  3. 接口的成员变量和成员方法只能public(或缺省不写),效果一样,都是public
  4. 实现接口的类必须全部实现接口中的方法(父类的实现也算,一般有通过基类实现接口中个异性不大的方法来做为适配器的做法)

关键字:final

  1. 可用于修饰:成员变量,非抽象类(不能与abstract同时出现),非抽象的成员方法,以及方法参数
  2. final方法:不能被子类的方法重写,但可以被继承;
  3. final类:表示该类不能被继承,没有子类;final类中的方法也无法被继承.
  4. final变量:表示常量,只能赋值一次,赋值后不能被修改.final变量必须定义初始化;
  5. final不能用于修饰构造方法;
  6. final参数:只能使用该参数,不能修改该参数的值;

关键字:static

  1. 可以修饰成员变量和成员方法,但不能修饰类以及构造方法;
  2. 被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享
  3. static变量和static方法一般是通过类名直接访问,但也可以通过类的实例来访问(不推荐这种访问方式)
  4. static变量和static方法同样适应java访问修饰符.用public修饰的static变量和static方法,在任何地方都可以通过类名直接来访问,但用private修饰的static变量和static方法,只能在声明的本类方法及静态块中访问,但不能用this访问,因为this属于非静态变量.

**static和final同时使用 **

  1. static final用来修饰成员变量和成员方法,可简单理解为“全局常量”!
  2. 对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
  3. 对于方法,表示不可覆盖,并且可以通过类名直接访问。

通过反射,用Class创建类对象

switch 能否用 String 做参数?

从 Java 7开始 switch 就能使用 String 做为参数。

在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。从Java 5开始,Java中引入了枚举类型,expr也可以是enum类型,从Java 7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。

Class<?> class = null;class = Class.forName("com.king.test");Test test = class.newInstance();//然后就可以调用test对象的方法了。

Object有哪些公用方法?

1.clone方法

保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。

主要是JAVA里除了8种基本类型传参数是值传递,其他的类对象传参数都是引用传递,我们有时候不希望在方法里讲参数改变,这是就需要在类中复写clone方法。

2.getClass方法

final方法,获得运行时类型。

3.toString方法

该方法用得比较多,一般子类都有覆盖。

4.finalize方法

该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。

5.equals方法

该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。

6.hashCode方法

该方法用于哈希查找,可以减少在查找中使用equals的次数,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。

一般必须满足obj1.equals(obj2)==true。可以推出obj1.hashCode()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。

如果不重写hashCode(),在HashSet中添加两个equals的对象,会将两个对象都加入进去。

7.wait方法

wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。

调用该方法后当前线程进入睡眠状态,直到以下事件发生。

(1)其他线程调用了该对象的notify方法。

(2)其他线程调用了该对象的notifyAll方法。

(3)其他线程调用了interrupt中断该线程。

(4)时间间隔到了。

此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。

8.notify方法

该方法唤醒在该对象上等待的某个线程。

9.notifyAll方法

该方法唤醒在该对象上等待的所有线程。

继承:继承是从已有的类得到继承信息创建新类的过程。提供继承信息的类被称为父类;得到继承信息的类被称为子类。继承让变化中的软件系统有了一定的可延续性,同时继承也是封装程序中可变因素的重要手段。封装:在类中编写的方法是对实现细节的封装,编写一个类就是对数据和数据操作的封装。封装就是隐藏一切可隐藏的东西,只向外界提供最简单的接口。多态:多态是指允许不同子类型的对象堆同一消息做出不同的响应。编译时多态:是指根据参数列表不同来区分不同的函数,编译之后就会变成两个函数,主要指方法的重载。运行时多态:,指在程序运行期间判断所引用对象的实际类型,根据实际类型判断并调用相应的方法和属性。要实现多态需要做两件事:1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2. 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

foreach与正常for循环效率对比。

直接for循环效率最高,其次是迭代器和 ForEach操作。作为语法糖,其实 ForEach 编译成 字节码之后,使用的是迭代器实现的,反编译后,testForEach方法如下:

public static void testForEach(List list) {  
    for (Iterator iterator = list.iterator(); iterator.hasNext();) {  
        Object t = iterator.next();  
        Object obj = t;  
    }  
}  

可以看到,只比迭代器遍历多了生成中间变量这一步,因为性能也略微下降了一些。

线程存在于进程中,每个进程都至少一个线程。线程共享进程的资源,包括内存和打开的文件。进程和线程是并发编程的两个基本的执行单元。在 Java 中,并发编程主要涉及线程。java实现多线程的两种方式,1.继承Thread类,2.实现Runnable接口。

基本数据类型

图片 1进程的基本状态及其转换一个线程可以处于以下四种状态之一:1.新建。当线程被创建时,它只会短暂的处于这种状态。此时它已经分配了必须的系统资源,并执行了初始化。此刻线程已经有资格获取CPU时间了,之后调度器把这个线程转变为可运行状态或者阻塞态。2.就绪。在这种状态下,只要调度器把时间片分配给线程,线程就可以运行。也就是说,在任意时刻,线程可以运行或者不运行。只要调度器能分配时间片给线程,他就可以运行,这不同于死亡和阻塞状态。3.阻塞。线程能够运行,但是有个条件阻止它的运行。当线程处于阻塞状态时,调度器会忽略线程,不会分配给线程CPU时间。直到线程重新进入就绪态,它才有可能执行操作。4.死亡。处于死亡或者终止状态的线程不再是可调度的。并且再也不会得到CPU时间。任务死亡的通常方式是从run()方法返回,但是任务线程还可以被中断。Java中使用synchronized关键字来实现同步。run系统通过调用start()方法来启动一个线程,此时该线程处于就绪态。直接调用run()方法,这会被当成一个普通的函数调用,程序中仍然只有主线程一个线程。wait()和notify()在synchronized代码被执行期间,线程可以调用对象的wait()方法,释放对象锁,进入等待状态,直到其他线程调用此对象的 notify()方法或 notifyAll()方法。notify()唤醒等待队列的第一个线程,并运行它去获取锁。notifyAll(),唤醒等待队列的全部线程,让他们去竞争锁。Java 5加入了Lock接口以及他的一个实现类ReentrantLock。lock()方法。以阻塞的方式获取锁。如果获得到锁,立即返回;如果别的线程持有锁,则当前线程等待,直到获取锁之后返回。tryLock(),非阻塞形式获取锁,如果获取到返回true,没有获取到,返回false。sleepsleep不会释放锁,wait会释放锁。joinjoin方法用线程对象调用,如果在一个线程A中调用另一个线程B的join方法,线程A将会等待线程B执行完毕后再执行。this和superthis用来指向当前实例的对象,用来区分成员变量和方法的形参,如果方法的形参和成员变量的名字相同的时候,方法的形参会覆盖成员变量。super用来访问父类的方法或成员变量。volatile使用这个修饰符修饰的变量,程序在读取它的值时直接从内存中获取,一定是最新值。而不是从缓存中获取。

基本数据类型 int char long 等各占多少字节

类型 位数 字节数
boolean 8(数组)或32(单个变量) 1(数组)或4(单个变量)
byte 8 1
short 16 2
int 32 4
long 64 8
float 32 4
double 64 8
char 16 2

PS:单个的 boolean 类型变量在编译的时候是使用的 int 类型。而对于 boolean 类型的数组时,在编译的时候是作为 byte。

值传递和引用传递值传递。在方法调用过程中,实参将自己的值传递给形参使用,虽然两者的值完全相等,但是在内存中确实完全不同的位置,对形参的改变不会影响到实参。引用传递。在方法调用中,传递的是对象的地址,这是形参和实参其实指向的是同一块存储单元。在Java中,8种基本数据类型都是值传递,包装类都是值传递。Math中的round、ceil和floorround四舍五入,返回结果int。ceil向上取整,返回double。floor向下取整,返回double。Java I/O流字节流:InputStream和outputStream字符流:Reader和WriterSocket流套接字,使用TCP协议。提供面向连接的服务。提供可靠连接。Java为TCP连接提供了两个类,Socket和ServerSocket类。一个Socket实例代表了TCP连接的一个客户端,一个ServerSocket代表一个服务端,一般在程序中会有多个客户端,一个服务端。客户端向服务端发送连接请求,服务端的ServerSocket实例监听来自客户端的连接请求,并为每个请求创建新的socket实例并开启新线程。serverSocket调用eccept()方法监听。每个Socket实例会关联一个InputStream和OutputStream。通过将数据写入OutputStream发送数据,通过InputStream接收数据。通信完成记得关闭连接。数据报套接字,使用UDP协议。面向非连接的,不可靠连接。服务端和客户端都是使用DatagramSocket的send()和receive()方法接收和发送数据。由于UDP是无连接的,所以服务端不用等待客户端请求连接。UDP服务器为所有通信使用同一套接字。如果数据在传输过程中发生丢失,那么程序会一直阻塞在recevice()处。为避免这个问题,可以设置recevice()的最长阻塞时间,并指定重发数据报的次数。堆和栈Java中,基本数据类型的变量以及对象的引用变量,内存都分配在栈上。而引用类型的变量,内存都分配在堆上或者常量池。关于CollectionList、Queue、Set、Stack继承自Collection接口。Set,其中的元素不能重复。线程不同步。有两个实现类:HashSet和TreeSet。TreeSet实现了SortedSet接口,底层的数据结构是二叉树,是有序的。List,ArrayList和Vector是基于数组实现的,只在末端添加删除元素快,随机访问快,Vector是线程安全的。LinkedList是基于双向链表的,在指定位置插入数据速度快。Collection包结构,与Collections的区别Collection是一个接口,他是Set、List等容器的父接口;Collections是一个工具类,提供一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等。Map集合这个集合是存储键值对的,实现类有:HashTable,底层是哈希表数据结构,不可存入null键和null值,线程同步。HashMap,底层是哈希表数据结构,可以存入null键和null值,线程不同步,效率较高。TreeMap,底层是二叉树数据结构,实现了SortMap接口,线程不同步,可以用于map集合中对键进行排序。String类String对象是不可变类型,一般返回类型为String的String方法每次返回的都是新的对象。String对象的比较方式:==内存比较,直接比较两个引用所指向的内存值,精确简介直接明了。equals字符串值比较,比较两个引用所指对象字面值是否相等。Object有哪些公用方法1.clone方法,实现对象的浅复制,只有实现了Cloneable接口才能调用 该方法。2.getClass方法,final方法,获得运行时类型。3.toString方法,一般由子类覆盖。4.finalize方法,用于释放资源,因为无法确定该方法什么时候被调用,很少有。5.equals方法。很重要的一个方法,用于比较,子类一般需要重写。6.hashCode方法,该方法用于哈希查找,可以减少在查找中使用equals的次数,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。7.wait方法,调用该方法后当前线程进入睡眠状态,直到以下事件发生.其他线程调用了该对象的notify方法或notifyAll方法。.其他线程调用了interrupt中断该线程。wait(long time)设置中的时间间隔到了。此时如果线程被中断就抛出一个InterruptedException异常,或者是处于可以被调度的状态。8.notify,唤醒等待该对象的某个线程。9.notifyAll,唤醒等待该对象的所有线程。ThrowableThrowable是Java中所有错误和异常的超类。包含两个子类,Error和Exception。RuntimeException是Exception的一个子类,表示可能在Java虚拟机正常运行期间抛出的异常的超类,编译器不会检查Error 和RuntimeException,要通过修改代码来避免这些情况。checkException,被检查的异常,Exception类本身,以及Exception的子类中除了RuntimeException之外的其他子类都属于被检查异常,这些异常Java编译器会检查它。此类异常,要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。

int 和 integer 的区别

  1. int是基本的数据类型;
  2. Integer是int的封装类;
  3. int和Integer都可以表示某一个数值;
  4. int和Integer不能够互用,因为他们两种不同的数据类型;

String

是否可以继承String类?

不能,因为 String 为 final 类。

swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?

  • switch语句中的表达式只能是byte,short,char ,int以及枚举(enum),所以当表达式是byte的时候可以隐含转换为int类型,而long字节比int字节多,不能隐式转化为int类型,所以switch语句可以用在byte上而不可以用在long上。
  • 由于在JDK7.0中引入了新特性,所以switch语句可以接收一个String类型的值,switch语句可以作用在String上。

常量final string str=“ab”可不可以变成”abd”,为什么?

不能,因为使用 final 修饰的变量不可改变。

StringBuffer的作用?

StringBuffer类和String一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和String不同,所以StringBuffer在进行字符串处理时,不生成新的对象,在内存使用上要优于String类。

所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入、删除等操作,使用StringBuffer要更加适合一些。

String StringBuffer StringBuilder 的区别

三者在执行速度方面的比较:StringBuilder > StringBuffer > String

  • String 字符串常量,不可变
  • StringBuffer 字符串变量(线程安全)
  • StringBuilder 字符串变量(非线程安全)

和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。

StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。

由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。


简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后,JVM 的 GC 就会开始工作,那速度是一定会相当慢的。

而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:

String S1 = “This is only a”   “ simple”   “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”); 

你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个 String S1 = “This is only a” “ simple” “test”; 其实就是: String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:

String S2 = “This is only a”; 
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2  S3   S4; 

这时候 JVM 会规规矩矩的按照原来的方式去做。在大部分情况下 StringBuffer > String。

StringBuffer

Java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。

可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。

StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。

例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“starlet”。

在大部分情况下 StringBuilder > StringBuffer

java.lang.StringBuilder

java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同

String s = new String(“abc”); new了几个对象

两个

数组有没有length()这个方法? String有没有length()这个方法?

  • 数组中没有length()这个方法,但是数组中有length这个属性。用来表示数组的长度。
  • String中有length()这个方法。用来得到字符串的长度。

String 源码分析

参考:

String源码分析

内部类

Static Inner Class 和 Inner Class 的不同

  • 静态内部类不持有外部类的引用;非静态内部类持有外部类的引用。
  • 静态内部类可以有静态成员(方法、属性),而非静态内部类则不能有静态成员(方法、属性)。
  • 静态内部类只能访问外部类的静态成员和静态方法,而非静态内部类则可以访问外部类的所以成员(方法、属性)。
  • 实例化一个静态内部类不依赖于外部类的实例,直接实例化内部类对象;实例化一个非静态内部类依赖于外部类的实例,通过外部类的实例生成内部类的实例。
  • 调用静态内部类的方法或静态变量,直接通过类名调用。

内部类机制

为什么内部类拥有外部类的所有元素的访问权?
当某个外围类对象创建一个内部连对象时,内部类对象必定会捕获一个指向那个外围类对象的引用。内部类对象只能在与其外部类对象关联的情况下才能被创建(在内部类非static时),构建内部类需要一个外部类的引用,内部类正是利用这个引用去访问外部类的。

内部类的种类
按照内部类所在的位置不同,内部类可以分为以下几种:

  1. 成员内部类
  2. 方法内部类
  3. 匿名内部类
  4. 静态内部类

内部类的作用?

  • 内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。
  • 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
  • 创建内部类对象的时刻并不依赖与外围类对象的创建。
  • 内部类并没有令人迷惑的“is-a”关系,它就是一个独立的实体。
  • 内部类提供了更好的封装,除了外围类,其他类都不能访问。

静态内部类、内部类、匿名内部类,为什么内部类会持有外部类的引用?持有的引用是this?还是其它?

静态内部类:使用static修饰的内部类

匿名内部类:使用new生成的内部类

因为内部类的产生依赖于外部类,持有的引用是类名.this。

继承

父类的静态方法能否被子类重写

不能。

父类的静态方法是不能被子类重写的,其实重写只能适用于实例方法,不能用于静态方法,对于上面这种静态方法而言,我们应该称之为隐藏。

Java静态方法形式上可以重写,但从本质上来说不是Java的重写。因为静态方法只与类相关,不与具体实现相关。声明的是什么类,则引用相应类的静态方法(本来静态无需声明,可以直接引用)。并且static方法不是后期绑定的,它在编译期就绑定了。换句话说,这个方法不会进行多态的判断,只与声明的类有关。

抽象类和接口

抽象类总结规定

  • 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
  • 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  • 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
  • 构造方法,类方法(用static修饰的方法)不能声明为抽象方法。
  • 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

接口与类的区别

  • 接口不能用于实例化对象。
  • 接口没有构造方法。
  • 接口中所有的方法必须是抽象方法。
  • 接口不能包含成员变量,除了 static 和 final 变量。
  • 接口不是被类继承了,而是要被类实现。
  • 接口支持多重继承。

接口特性

  • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
  • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
  • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

抽象类和接口的区别

  1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  3. 接口中不能有静态代码块以及静态方法(用 static 修饰的方法),而抽象类中可以有静态代码块和静态方法。注意:Java 8的新特性允许接口中存在静态方法。
  4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
  5. 接口中不能有构造函数和main方法;而抽象类中可以有。

  1. 默认的方法实现。抽象类可以有默认的方法实现完全是抽象的。接口根本不存在方法的实现。
  2. 实现。使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现。
  3. 构造器。抽象类可以有构造器。接口不能有构造器。
  4. 与正常Java类的区别。除了你不能实例化抽象类之外,它和普通Java类没有任何区 接口是完全不同的类型
  5. 访问修饰符。抽象方法可以有public、protected和default这些修饰符 接口方法默认修饰符是public。你不可以使用其它修饰符。
  6. main方法。抽象方法可以有main方法并且我们可以运行它。接口没有main方法,因此我们不能运行它。
  7. 多继承。抽象类在java语言中所表示的是一种继承关系,一个子类只能存在一个父类,但是可以存在多个接口。
  8. 速度。抽象类比接口速度要快。接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。
  9. 添加新方法。如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。如果你往接口中添加方法,那么你必须改变实现该接口的类。

语法层面上的区别

  1. 抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
  2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
  3. 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
  4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

设计层面上的区别

  1. 抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。
  2. 设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。

接口的意义

  • 提供一个规范,要求类必须实现指定的方法。
  • 解决 Java 中的单继承问题,可以用接口来实现多继承的功能,简单化多重继承中继承树的复杂程度。
  • 增强程序的扩展性。

抽象类的意义

为其子类提供一个公共的类型、封装子类中的重复内容、定义抽象方法,子类虽然有不同的实现,但是定义是一致的。

接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concreteclass)?

  1. 接口可以继承接口。但是要使用extends,而不是用implements。
  1. 抽象类可以实现接口。比如java.util中的AbstractCollection类就是实现的Collection接口。
  1. 抽象类可以继承实体类。

下面这段执行无误的代码说明的所有的问题:

interface MyInterface {

}

interface AnotherInterface extends MyInterface {

}

class EntityClass {

}

abstract class AbstractClass extends EntityClass implements MyInterface {

}

接口的继承

一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。

下面的Sports接口被Hockey和Football接口继承:

// 文件名: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

// 文件名: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

// 文件名: Hockey.java
public interface Hockey extends Sports
{
   public void homeGoalScored();
   public void visitingGoalScored();
   public void endOfPeriod(int period);
   public void overtimePeriod(int ot);
}

Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。

相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。

接口的多重继承

在Java中,类的多重继承是不合法,但接口允许多重继承。在接口的多重继承中extends关键字只需要使用一次,在其后跟着继承接口。如下所示:

public interface Hockey extends Sports, Event

以上的程序片段是合法定义的子接口,与类不同的是,接口允许多重继承,而 Sports及 Event 可能定义或是继承相同的方法

abstract的method是否可同时是static,是否可同时是native,是否可同时时final,是否可同时是synchronized?

都不行。

abstract的method不可以是static的,因为抽象的方法是要被子类实现的,而static与子类扯不上关系!

native方法表示该方法要用另外一种依赖平台的编辑语言实现的,不存在者被子类实现的问题,所以,他也不能是抽象的,不能与abstract混用。

关于synchronized中avstract合用的问题,我觉得也不行,因为我觉得 synchronized应该是作用在一个具体的方法上才有意义。而且,方法上的synchronized同步所使用的同步锁对象是this,而抽象方法 上无法确定this是什么。

final

类使用 final 修饰符的用处?

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。

参考:http://www.cnblogs.com/dolphin0520/p/3736238.html

finally final finalize的作用?

final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承。

finally是异常处理语句结构的一部分,表示总是执行。

finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。

TAG标签:
版权声明:本文由澳门新葡8455手机版发布于计算机编程,转载请注明出处:面向对象下,Java基础知识复习