首页 热点专区 小学知识 中学知识 出国留学 考研考公
您的当前位置:首页正文

Java基础(一)

2024-12-09 来源:要发发知识网

Java要点1

JAVA基础

1.对抽象、继承、多态的理解

2.泛型的作用及使用场景

1.Java泛型是JDK1.5版本开始引入的一个概念,它可以将Java类型抽象,提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的数据类型。

public  static void main(String[] args){
    //不使用泛型
    List listA=new ArrayList();
    listA.add(2);
    listA.add("a");
    int temp0=(int)listA.get(0);
    int temp1=(int)listA.get(1);
}

当从list中取值时,取出来的所有元素都是Object类型,需要通过类型转换强行转成自己所需的类型,但由于此时值类型并不匹配,无法将String类型转换成int,因此该方法会抛出ClassCastException异常。为了避免这种情况的发生,提早发现问题,我们需要在使用时加上泛型,如下例:

public static void main(String[] args){
    //使用泛型
    List<Integer> listA= new ArrayList<Integer>();
    listA.add(1);
    //listA.add("a");//提示编译错误
    int temp1=listA.get(0);
}

以上的例子就是泛型作用最大的体现。假设没有泛型,当我们需要调用同一个方法返回多种不同类型的结果对象时,由于return语句只能返回单个对象,我们只能创建一个对象,用这个对象包含所有需要返回的对象;亦或者是return一个父类,所有返回的类型都必须继承自这个父类。但这两种方法都会有一堆问题,而泛型帮我们解决了这些问题,同时,通过泛型我们还可以保证在编译器就可以确保类型安全。

2.泛型种类
泛型命名时通常使用单个的大写字母,例如E (Element,表示元素),Map中时常使用的K (Key),V (Value)等。

3.泛型擦除
看完常见的几种泛型使用,我们再来了解下泛型最重要的一个概念:泛型擦除。
Java中泛型类型是作为第二类类型处理的,只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为他们的非泛型边界。例如List这样的将被擦除为List,而普通的类型变量在未指定边界的情况下将被擦除成Object。概念讲完,我们依旧通过一个小例子来直观的感受下什么是泛型的擦除。

public static void main(String[] args){
    List<Integer> list1=new ArrayList<Integer>();
    List<String> list2 =new ArrayList<String>();
    System.out.println(list1.getClass().equals(list2.getClass()));//结果为true
}

3.枚举的特点及使用场景

枚举跟表示一组常量的最大的区别就是拥有类的属性。

1.背景

在java语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具有int常量。之前我们通常利用public final static 方法定义的代码如下,分别用1 表示春天,2表示夏天,3表示秋天,4表示冬天。

public class Season {
    public static final int SPRING = 1;
    public static final int SUMMER = 2;
    public static final int AUTUMN = 3;
    public static final int WINTER = 4;
}

这种方法称作int枚举模式。可这种模式有什么问题呢,我们都用了那么久了,应该没问题的。通常我们写出来的代码都会考虑它的安全性、易用性和可读性。 首先我们来考虑一下它的类型安全性。当然这种模式不是类型安全的。比如说我们设计一个函数,要求传入春夏秋冬的某个值。但是使用int类型,我们无法保证传入的值为合法。代码如下所示:

private String getChineseSeason(int season){
    StringBuffer result = new StringBuffer();
    switch(season){
        case Season.SPRING :
            result.append("春天");
            break;
        case Season.SUMMER :
            result.append("夏天");
            break;
        case Season.AUTUMN :
            result.append("秋天");
            break;
        case Season.WINTER :
            result.append("冬天");
            break;
        default :
            result.append("地球没有的季节");
            break;
    }
    return result.toString();
}

public void doSomething(){
    System.out.println(this.getChineseSeason(Season.SPRING));//这是正常的场景

    System.out.println(this.getChineseSeason(5));//这个却是不正常的场景,这就导致了类型不安全问题
}

程序getChineseSeason(Season.SPRING)是我们预期的使用方法。可getChineseSeason(5)显然就不是了,而且编译很通过,在运行时会出现什么情况,我们就不得而知了。这显然就不符合Java程序的类型安全。

2.定义

枚举类型(enum type)是指由一组固定的常量组成合法的类型。Java中由关键字enum来定义一个枚举类型。下面就是java枚举类型的定义。

public enum Season {
    SPRING, SUMMER, AUTUMN, WINER;
}

3.特点

Java定义枚举类型的语句很简约。它有以下特点:

  1. 使用关键字enum 2) 类型名称,比如这里的Season 3) 一串允许的值,比如上面定义的春夏秋冬四季 4) 枚举可以单独定义在一个文件中,也可以嵌在其它Java类中

除了这样的基本要求外,用户还有一些其他选择

  1. 枚举可以实现一个或多个接口(Interface) 6) 可以定义新的变量 7) 可以定义新的方法 8) 可以定义根据具体枚举值而相异的类

4 应用场景

以在背景中提到的类型安全为例,用枚举类型重写那段代码。代码如下:

public enum Season {
SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);

private int code;
private Season(int code){
    this.code = code;
}

public int getCode(){
    return code;
}
}
public class UseSeason {
/**
* 将英文的季节转换成中文季节
 * @param season
 * @return
 */
public String getChineseSeason(Season season){
    StringBuffer result = new StringBuffer();
    switch(season){
        case SPRING :
            result.append("[中文:春天,枚举常量:" + season.name() + ",数据:" + season.getCode() + "]");
            break;
        case AUTUMN :
            result.append("[中文:秋天,枚举常量:" + season.name() + ",数据:" + season.getCode() + "]");
            break;
        case SUMMER :
            result.append("[中文:夏天,枚举常量:" + season.name() + ",数据:" + season.getCode() + "]");
            break;
        case WINTER :
            result.append("[中文:冬天,枚举常量:" + season.name() + ",数据:" + season.getCode() + "]");
            break;
        default :
            result.append("地球没有的季节 " + season.name());
            break;
    }
    return result.toString();
}

public void doSomething(){
    for(Season s : Season.values()){
        System.out.println(getChineseSeason(s));//这是正常的场景
    }
    //System.out.println(getChineseSeason(5));
    //此处已经是编译不通过了,这就保证了类型安全
}

public static void main(String[] arg){
    UseSeason useSeason = new UseSeason();
    useSeason.doSomething();
}
}

[中文:春天,枚举常量:SPRING,数据:1] [中文:夏天,枚举常量:SUMMER,数据:2] [中文:秋天,枚举常量:AUTUMN,数据:3] [中文:冬天,枚举常量:WINTER,数据:4]

这里有一个问题,为什么我要将域添加到枚举类型中呢?目的是想将数据与它的常量关联起来。如1代表春天,2代表夏天。

6 用法

用法一:常量

public enum Color {
    RED, GREEN, BLANK, YELLOW
}

用法二:switch

enum Signal {
    GREEN, YELLOW, RED
}
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
    switch (color) {
    case RED:
        color = Signal.GREEN;
        break;
    case YELLOW:
        color = Signal.RED;
        break;
    case GREEN:
        color = Signal.YELLOW;
        break;
    }
}
}

用法三:向枚举中添加新方法

public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
    this.name = name;
    this.index = index;
}
// 普通方法
public static String getName(int index) {
    for (Color c : Color.values()) {
        if (c.getIndex() == index) {
            return c.name;
        }
    }
    return null;
}
// get set 方法
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public int getIndex() {
    return index;
}
public void setIndex(int index) {
    this.index = index;
}
}

用法四:覆盖枚举的方法

public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
    this.name = name;
    this.index = index;
}
//覆盖方法
@Override
public String toString() {
    return this.index+"_"+this.name;
}
}

用法五:实现接口

public interface Behaviour {
void print();
String getInfo();
}
public enum Color implements Behaviour{
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
    this.name = name;
    this.index = index;
}
//接口方法
@Override
public String getInfo() {
    return this.name;
}
//接口方法
@Override
public void print() {
    System.out.println(this.index+":"+this.name);
}
}

用法六:使用接口组织枚举

public interface Food {
enum Coffee implements Food{
    BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO
}
enum Dessert implements Food{
    FRUIT, CAKE, GELATO
}
}

4.线程sleep和wait的区别

sleep()方法

sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;

sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。

在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。

wait()方法

wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁);其他线程可以访问;

wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前等待池中的线程。

wiat()必须放在synchronized block中,否则会在program runtime时扔出”java.lang.IllegalMonitorStateException“异常。

所以sleep()和wait()方法的最大区别是:

sleep()睡眠时,保持对象锁,仍然占有该锁;

而wait()睡眠时,释放对象锁。

但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException(但不建议使用该方法)。

5.JAVA反射机制

要让Java程序能够运行,就得让Java类被Java虚拟机加载。Java类如果不被Java虚拟机加载就不能正常运行。正常情况下,我们运行的所有的程序在编译期时候就已经把那个类被加载了。

Java的反射机制是在编译时并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用的是在编译期并不知道的类。这样的编译特点就是java反射。

6.weak/soft/strong引用的区别

Java中有如下四种类型的引用:

强引用(Strong Reference)

弱引用(WeakReference)

软引用(SoftReference)

虚引用(PhantomReference)

强引用是我们在编程过程中使用的最简单的引用,如代码String s=”abc”中变量s就是字符串对象”abc”的一个强引用。任何被强引用指向的对象都不能被垃圾回收器回收,这些对象都是在程序中需要的。弱引用使用java.lang.ref.WeakReference class 类来表示,你可以使用如下代码创建弱引用:

Counter counter = new Counter(); // strong reference - line 1
WeakReference<Counter> weakCounter = new WeakReference<Counter>(counter); //weak reference
counter = null; // now Counter object is eligible for garbage collection

现在只要你给强引用对象counter赋空值null,该对象就可以被垃圾回收器回收。因为该对象此时不再含有其他强引用,即使指向该对象的弱引用weakCounter也无法阻止垃圾回收器对该对象的回收。相反的,如果该对象含有软引用,Counter对象不会立即被回收,除非JVM需要内存。Java中的软引用使用java.lang.ref.SoftReference类来表示,你可以使用如下代码创建软引用:

Counter prime = new Counter(); // prime holds a strong reference – line 2
SoftReference soft = new SoftReference(prime) ; //soft reference variable has SoftReference to Counter Object created at line 2
prime = null; // now Counter object is eligible for garbage collection but only be collected when JVM absolutely needs memory

强引用置空之后,代码的第二行为对象Counter创建了一个软引用,该引用同样不能阻止垃圾回收器回收对象,但是可以延迟回收,与弱引用中急切回收对象不同。鉴于软引用和弱引用的这一区别,软引用更适用于缓存机制,而弱引用更适用于存贮元数据。另一个使用弱引用的例子是WeakHashMap,它是除HashMap和TreeMap之外,Map接口的另一种实现。WeakHashMap有一个特点:map中的键值(keys)都被封装成弱引用,也就是说一旦强引用被删除,WeakHashMap内部的弱引用就无法阻止该对象被垃圾回收器回收。

虚引用是java.lang.ref package包中第三种可用的引用,使用java.lang.ref.PhantomReference类来表示。拥有虚引用的对象可以在任何时候被垃圾回收器回收。和弱引用和软引用相似,你可以通过如下代码创建虚引用:

DigitalCounter digit = new DigitalCounter(); // digit reference variable has strong reference – line 3
PhantomReference phantom = new PhantomReference(digit); // phantom reference to object created at line 3

digit = null;

一旦移除强引用,第三行的DigitalCounter对象可以在任何时候被垃圾回收器回收。因为只有一个虚引用指向该对象,而虚引用无法阻止垃圾回收器回收对象。

7.Object的hashCode()与equals()的区别和作用

显示全文