1、API概述
1.1 什么是API
API(Application Programming Interface),应用程序编程接口。
Java API是一本程序员的字典 ,是JDK中提供给我们使用的类的说明文档。这些类将底层的代码实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可。所以我们可以通过查询API的方式,来学习Java提供的类,并得知如何使用它们。
1.2 由Scanner类使用API
1.2.1 什么是Scanner类
一个可以解析基本类型和字符串的简单文本扫描器。 例如,以下代码使用户能够从 System.in 中读取一个数:
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
1.2.2 引用类型使用步骤
导包
- 使用import关键字导包,在类的所有代码之前导包,引入要使用的类型,java.lang包下的所有类无需导入。
格式: import 包名.类名;
创建对象
- 使用该类的构造方法,创建一个该类的对象。
格式: 类名 对象名 = new 类名(参数列表);
调用方法
- 调用该类的成员方法,完成指定功能。
格式: 对象名.方法名();
1.2.3 Scanner使用步骤
查看类
- java.util.Scanner :该类需要import导入后使用。
查看构造方法
- public Scanner(InputStream source) : 构造一个新的 Scanner ,它生成的值是从指定的输入流扫描的。
查看成员方法
- public int nextInt() :将输入信息的下一个标记扫描为一个 int 值。
- public String nextLine(): 此扫描器执行当前行,并返回跳过的输入信息。
1.3 Scanner使用案例
package com.suyv.scanner;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-08 14:46
*@Description: TODO
*/
public class ScannerTest {
public static void main(String[] args) throws FileNotFoundException {
File file = new File("F:\\a.txt");
Scanner scan = new Scanner(file);
String str = scan.next();
System.out.println(str);
File file1 = new File("F:\\a1.txt");
Scanner scan1 = new Scanner(file1,"GBK");
String str1 = scan1.next();
System.out.println(str1);
// hasNextLine() 判断是否有下一行,有则返回true,没有则返回false
while (scan1.hasNextLine()){
// nextLine() 整行输出
System.out.println(scan1.nextLine());
}
}
}
2、Object类
2.1 如何理解根父类
类 java.lang.Object是类层次结构的根类,即所有其它类的父类。每个类都使用 Object作为超类。
- Object类型的变量与除Object以外的任意引用数据类型的对象都存在多态引用
method(Object obj){…} //可以接收任何类作为其参数
Person o = new Person();
method(o);
- 所有对象(包括数组)都实现这个类的方法。
- 如果一个类没有特别指定父类,那么默认则继承自Object类。例如:
public class Person {
...
}
//等价于:
public class Person extends Object {
...
}
2.2 Object类的方法
根据JDK源代码及Object类的API文档,Object类当中包含的方法有11个。这里我们主要关注其中的6个:
方法名 | 说明 |
protected Object clone() | 创建并返回此对象的副本。 |
public boolean equals(Object abj) | 指示其他某个对象是否与此对象“相等”。 |
protected void finalize() | 当垃圾收集确定不再有对该对象的引用时, 垃圾收集器在对象上调用该对象。 |
public final Class<?> getClass() | 返回此 Object的运行时类。 |
public int hashCode() | 返回对象的哈希码值。 |
public void notify() | 唤醒正在等待对象监视器的单个线程。 |
public void notifyAll() | 唤醒正在等待对象监视器的所有线程。 |
public String toString() | 返回对象的字符串表示形式。 |
public void wait() | 导致当前线程等待,直到另一个线程调用 该对象的 notify()方法或 notifyAll()方法。 |
public void wait(long timeout) | 导致当前线程等待,直到另一个线程调用 notify()方法或该对象的 notifyAll()方法, 或者指定的时间已过。 |
public void wait(long timeout, int nanos) | 导致当前线程等待,直到另一个线程调用该对象 的 notify()方法或 notifyAll()方法,或者某些其他 线程中断当前线程,或一定量的实时时间。 |
2.2.1 equals()(重点)
==:
基本类型比较值:只要两个变量的值相等,即为true。
int a=5;
if(a==6){…}
引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回true。
Person p1=new Person();
Person p2=new Person();
if (p1==p2){…}
用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错
equals():所有类都继承了Object,也就获得了equals()方法。还可以重写。
只能比较引用类型,Object类源码中equals()的作用与“==”相同:比较是否指向同一个对象。
格式:obj1.equals(obj2)
特例:当用equals()方法进行比较时,对类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象;
原因:在这些类中重写了Object类的equals()方法。
当自定义使用equals()时,可以重写。用于比较两个对象的“内容”是否都相等
重写equals()方法的原则
- 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
- 自反性:x.equals(x)必须返回是“true”。
- 传递性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
- 一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。
重写举例:
class User{
private String host;
private String username;
private String password;
public User(String host, String username, String password) {
super();
this.host = host;
this.username = username;
this.password = password;
}
public User() {
super();
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [host=" + host + ", username=" + username + ", password=" + password + "]";
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (host == null) {
if (other.host != null)
return false;
} else if (!host.equals(other.host))
return false;
if (password == null) {
if (other.password != null)
return false;
} else if (!password.equals(other.password))
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
}
面试题:==和equals的区别
- == 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
- equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。
- 具体要看自定义类里有没有重写Object的equals方法来判断。
- 通常情况下,重写equals方法,会比较类中的相应属性是否都相等。
练习1:
int it = 65;
float fl = 65.0f;
System.out.println(“65和65.0f是否相等?” + (it == fl)); // true
char ch1 = 'A'; char ch2 = 12;
System.out.println("65和'A'是否相等?" + (it == ch1));// true
System.out.println("12和ch2是否相等?" + (12 == ch2));// true
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println("str1和str2是否相等?"+ (str1 == str2));// false
System.out.println("str1是否equals str2?"+(str1.equals(str2)));// true
System.out.println(“hello” == new java.util.Date()); // 报错 类型不匹配
2.2.2 toString()(重点)
方法签名:public String toString()
① 默认情况下,toString()返回的是“对象的运行时类型 @对象的hashCode值的十六进制形式"
② 在进行String与其它类型数据的连接操作时,自动调用toString()方法
Date now=new Date();
System.out.println(“now=”+now); //相当于
System.out.println(“now=”+now.toString());
③ 如果我们直接System.out.println(对象),默认会自动调用这个对象的toString()
因为Java的引用数据类型的变量中存储的实际上时对象的内存地址,但是Java对程序员隐藏内存地址信息,所以不能直接将内存地址显示出来,所以当你打印对象时,JVM帮你调用了对象的toString()。
④ 可以根据需要在用户自定义类型中重写toString()方法 如String 类重写了toString()方法,返回字符串的值。
s1="hello";
System.out.println(s1); //相当于System.out.println(s1.toString());
例如自定义的Person类:
public class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
2.2.3 clone()
//Object类的clone()的使用
public class CloneTest {
public static void main(String[] args) {
Animal a1 = new Animal("花花");
try {
Animal a2 = (Animal) a1.clone();
System.out.println("原始对象:" + a1);
a2.setName("毛毛");
System.out.println("clone之后的对象:" + a2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
class Animal implements Cloneable{
private String name;
public Animal() {
super();
}
public Animal(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal [name=" + name + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
2.2.4 finalize()
- 当对象被回收时,系统自动调用该对象的 finalize() 方法。(不是垃圾回收器调用的,是本类对象调用的)
- 永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。
- 什么时候被回收:当某个对象没有任何引用时,JVM就认为这个对象是垃圾对象,就会在之后不确定的时间使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize()方法。
- 子类可以重写该方法,目的是在对象被清理之前执行必要的清理操作。比如,在方法内断开相关连接资源。
- 如果重写该方法,让一个新的引用变量重新引用该对象,则会重新激活对象。
- 在JDK 9中此方法已经被标记为过时的。
public class FinalizeTest {
public static void main(String[] args) {
Person p = new Person("Peter", 12);
System.out.println(p);
p = null;//此时对象实体就是垃圾对象,等待被回收。但时间不确定。
System.gc();//强制性释放空间
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//子类重写此方法,可在释放对象前进行某些操作
@Override
protected void finalize() throws Throwable {
System.out.println("对象被释放--->" + this);
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
2.2.5 getClass()
public final Class<?> getClass():获取对象的运行时类型
因为Java有多态现象,所以一个引用数据类型的变量的编译时类型与运行时类型可能不一致,因此如果需要查看这个变量实际指向的对象的类型,需要用getClass()方法
public static void main(String[] args) {
Object obj = new Person();
System.out.println(obj.getClass());//运行时类型
}
2.6 hashCode()
public int hashCode():返回每个对象的hash值。
public static void main(String[] args) {
System.out.println("AA".hashCode()); //2080
System.out.println("BB".hashCode()); //2112
}
哈希值相同时equals不一定为true
equals为true时哈希值一定相同
面试题:
在重写equals方法的时侯,一定要重写hashcode方法吗?
当只重写equals方法,不重写hashCode时,违反规定:equals相等的对象必须具有相等的哈希码(因为hashCode的返回值还是按照Object类的规范:同一对象的hashCode值相同)。所以必须重写hashCode方法。
2.3 native关键字的理解
使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++等非Java语言实现的,并且被编译成了DLL,由Java去调用。
- 本地方法是有方法体的,用c语言编写。由于本地方法的方法体源码没有对我们开源,所以我们看不到方法体
- 在Java中定义一个native方法时,并不提供实现体。
2.3.1 为什么要用native方法
Java使用起来非常方便,然而有些层次的任务用java实现起来不容易,或者我们对程序的效率很在意时,例如:Java需要与一些底层操作系统或某些硬件交换信息时的情况。native方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解Java应用之外的繁琐的细节。
2.3.2 native声明的方法,对于调用者,可以当做和其他Java方法一样使用
native method的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。JVM将控制调用本地方法的所有细节。