java.lang.Object类是所有类的父类。
Object类定义了“对象”的基本“行为”,被子类继承这些行为。
万事万物皆对象。
一个类定义的时候,没有使用extends继承某个特定父类,那么该类的父类就是Object.
public class TestObject {
public static void main(String[] args) {
Object obj = new Object(); //public class Object
System.out.println(obj); //java.lang.Object@1b6d3586
Student stu = new Student("1001","alice" , 20);
// stu指代的对象的类型: stu.getClass()
Class aClass = stu.getClass(); // getClass()是Object中的一个方法。
System.out.println(aClass);// class seday1.Student
Object obj1 = new Student("1001" , "alice" , 20);
// ** 类.class得到的是类型的全名
System.out.println(Student.class);//class seday1.Student
if(obj1.getClass() == Student.class){
//System.out.println(true);
// 强制数据类型转换
Student stu2 = (Student) obj1 ;
System.out.println(stu2.getStuName());
}
print("hello");
print(new Date());
print(stu);
print(100);
}
// 写一个方法 ,参数可以是String , Date , int ,输出这些内容。
// *** String 是Object , Date 是Object , Integer 是Object
//
public static void print(Object str){
System.out.println(str);
}
Object类中的toString方法,用于返回对象成员变量的字符串表示。
Object类中的toString方法的定义:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
所有java类都有toString方法,通常会要求根据需要重写toString方法,返回更有意义的字符串。
System.out.println(obj) 等价于 System.out.println(obj.toString())
Object类的equals方法,用于判断对象是否相等。
Object类equals方法的定义:
自己和某个对象进行比较。
public boolean equals(Object obj) {
return (this == obj);
}
子类中可以根据实际需求,自定义比较规则,然后重写equals方法。
Object类的hashCode方法,根据对象的内容,返回一个整数值。
Returns a hash code value for the object。
一般重写了equals方法,就会要求重写hashCode方法。
Object类hashCode方法定义:
public native int hashCode()
import java.util.Objects;
// Student的父类: Object
// ** 重写父类的方法: toString , equals , hashCode
public class Student {
private String stuNo;
private String stuName;
private int stuAge;
public Student(){
}
public Student(String stuNo, String stuName, int stuAge) {
this.stuNo = stuNo;
this.stuName = stuName;
this.stuAge = stuAge;
}
@Override
public String toString() {
return "Student{" +
"stuNo='" + stuNo + '\'' +
", stuName='" + stuName + '\'' +
", stuAge=" + stuAge +
'}';
}
@Override // 表示该方法必须是重写父类的方法。也就是说方法名字,返回值,参数都是和父类一致
的方法。
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return stuAge == student.stuAge && Objects.equals(stuNo, student.stuNo)
&& Objects.equals(stuName, student.stuName);
}
@Override
public int hashCode() {
return Objects.hash(stuNo, stuName, stuAge);
}
public String getStuNo() {
return stuNo;
}
public void setStuNo(String stuNo) {
this.stuNo = stuNo;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getStuAge() {
return stuAge;
}
public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}
}
java.lang.String中封装的是字符串序列。
String在内存中采用Unicode编码方式,一个字符两个字节的定长。
String对象创建之后,这个对象的内容(即对象中的字符串序列不可变)不可变化。
public static void main(String[] args) {
String str = "nice to meet you";
str = str.toUpperCase();
System.out.println(str);
String str1 = "GOOD MORNING!";
str1 = str1.toLowerCase();
System.out.println(str1);//现在有4个字符串对象
java中可以使用直接量,创建字符串对象。
String str = "hello world";
出于性能的考虑,jvm会将这些字符串字面量的对象,保存在常量池中,对于重复出现的
字符串直接量,jvm优先在缓存中查找,如果存在就直接返回该对象。
String str1 = “nice to meet you .”;
String str2 = “nice to meet you .”;
System.out.println(s1 == s2) ;// true.
String str3 = new String(“nice to meet you .”);
System.out.println(s3 == s2) ;// false.
// 字符串不可变, 常量池。
public class StringDemo1 {
public static void main(String[] args) {
String str = "hello";
String str1 = "hello1";
System.out.println(str == str1); // false
String str2 = "hello";
System.out.println(str == str2); // true
String str3 = "hello" + "1"; // 编译的时候,会对这种直接量做运算。 运算的结
果是“hello1” ,常量池存在hello1. 所以就把hello1的地址,赋值给str3.
System.out.println(str1 == str3); // true
String str4 = str2 + "1"; // 运行的时候,才会拼字符串。 和常量池
的“hello1”的地址不等。
System.out.println(str4 == str1); // false
String str5 = new String("hello"); // new的对象,不在常量池中。
System.out.println(str == str5);// false .
}
}
字符串对象equals方法
// String重写了父类的equals方法
// equals :字符串内容相等则为true, 否则false.
// equalsIgnoreCase : 忽略大小写的比较。
public class StringDemo2 {
public static void main(String[] args) {
String str1 = "nice to meet you."; // ** 常用的
String str2 = new String("nice to meet you.");
if(str1.equals(str2)){
System.out.println("两个字符串相等");
}else{
System.out.println("两个字符串不等");
}
// 练习: 输入验证码, 忽略大小写比较。
// **equalsIgnoreCase
String yzm ="YUqq10";
Scanner scanner = new Scanner(System.in);
System.out.println("请输入验证码-"+yzm+":");
String userYzm = scanner.next();
if(yzm.equalsIgnoreCase(userYzm)){
System.out.println("输入正确。");
}else{
System.out.println("输入错误。");
}
}
}
字符串的大小写转换,长度,以什么开始,什么结尾
import java.util.Arrays;
import java.util.Locale;
import java.util.Scanner;
// String的一些常用方法
public class StringDemo3 {
public static void main(String[] args) {
String str = "nice to meet you.";
// 转为全部大写
str = str.toUpperCase();//
System.out.println(str);
String str1 = "GOOD MORING!";
// 转为全小写
str1 = str1.toLowerCase();
System.out.println(str1); // 现在有多少个字符串对象存在?4个
// 练习: 让用户输入用户名 ,去掉用户输入的字符串前后多余的空格。
// trim:去掉字符串前后多余的空格
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的用户名:");
String uname = " xxx "; // sc.nextLine(); // " fjm ... ";
System.out.println("你的名字:" + uname);
uname = uname.trim();
System.out.println("程序去掉前后空格:" + uname);
// 练习:判断 某个字符串是否以.com结尾 , www.开始
// startsWith : 以某个字符串开始。
// endsWith : 以某个字符串结束。
String bd = "www.baidu.com";
if(bd.startsWith("www.") && bd.endsWith(".com")){
System.out.println("是一个正确的域名");
}
// 练习:按字符串的长度排序
// 字符串.length(): 获取到字符的长度的方法
// 数组.length : 数组的属性。
String[] strs = {"tom" , "alice" , "rose" , "tom hanks" , "hi"};
for(int i=0 ; i < strs.length ; i++){
for(int j = 0 ; j < strs.length -1 ; j ++){
// 判断是否要交换位置
if(strs[j].length() > strs[j+1].length()){ // 前面大,后面小,
需要交换位置,把大的挪到后面去。
String temp = strs[j];
strs[j] = strs[j+1];
strs[j+1] = temp;
}
}
}
System.out.println("排序后的数组:" + Arrays.toString(strs));
// 练习:把数组元素全部转换为大写。
// for(int i = 0 ; i < strs.length ; i ++){
// strs[i] = strs[i].toUpperCase();
// }
// System.out.println("大写转换后的数组:" + Arrays.toString(strs));
// 练习:把数组元素全部转换为大写 ,但是原数组不变。
String[] strs1 = new String[strs.length];
for(int i = 0 ; i < strs.length ; i ++){
strs1[i] = strs[i].toUpperCase();
}
System.out.println("全大写的数组:" + Arrays.toString(strs1));
System.out.println(Arrays.toString(strs).toUpperCase());
}
}
字符串的索引位置相关的方法
// indexOf(String) , indexOf(String , int) , lastIndexOf( String , int)
public class StringDemo4 {
public static void main(String[] args) {
String str = "abctomabcdtomabcdtomabcd"; // 字符串中的每个字符都有一个对
应的索引位置。从0开始,到长度-1.
// 1.找到字符串tom
int index = str.indexOf("tom");
System.out.println("index:" + index); // 3.
//2. 找到字符串tom 的位置,指定从第5个位置开始找。
index = str.indexOf("tom" , 5) ;
System.out.println("index:" + index); // 10.
/*
str.indexOf(10);
str.indexOf('t');
*/
// 3. 最后一个tom
index = str.lastIndexOf("tom");
System.out.println("index:" + index); // 17
// 4. 找到tom , 从后往前找,从16开始
index = str.lastIndexOf("tom" ,16);
System.out.println("index:" + index); // 10
// 5. 找到"tom1"
index = str.indexOf("tom1"); // 方法找不到,返回-1,否则返回字符串所在的索
引位置
if(index != -1){
System.out.println("index:" + index);
}else{
System.out.println("没有找到这个字符串");
}
// 6. 练习。统计tom在str中出现的次数
int count = 0 ;
// str = "abctomabcdtomabcdtomabcd";
int i = 0 ;
while (true){
if(count == 0){
i = str.indexOf("tom" , 0 );
}else{
i = str.indexOf("tom" , i+3 );
}
if(i==-1){
break;
}
count ++;
}
System.out.println("count:" + count);
}
}
根据索引位置,找到对应位置的字符
// charAt的使用 : 根据索引位置,找到对应位置的字符。
public class StringDemo5 {
public static void main(String[] args) {
String str = "abc";
char c1 = str.charAt(0) ;
char c2 = str.charAt(1);
char c3 = str.charAt(2);
System.out.println(c1 + ":"+ c2 +":" + c3);
// char c4 = str.charAt(3); //
java.lang.StringIndexOutOfBoundsException: String index out of range: 3
// 练习:判断一个字符串是否为回文
String str1 = "上海自来水化来自海上";
boolean flag = true;// 标志是回文。
for(int i =0 ; i < str1.length() /2 ; i ++){
if(str1.charAt(i) != str1.charAt(str1.length()-i-1)){
//System.out.println("不是回文");
flag = false ;// 标志不是回文。
break;
}
}
if(flag == true){
System.out.println("是回文");
}else{
System.out.println("不是回文");
}
}
}
子字符串 , 获取字符串的字节数组
import jdk.management.resource.internal.inst.SocketOutputStreamRMHooks;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
// substring : 子字符串
// getBytes : 获取字节数组
public class StringDemo6 {
public static void main(String[] args) throws
UnsupportedEncodingException {
String str1 = "hello tom .";
// 1. 找其中的一部分
String substr = str1.substring(5);
System.out.println("substr:" + substr);
// 2. 找到tom :
substr = str1.substring(6 , 9); // tom - t对应的是6 。 m 对应的是8 ,
java中的指定索引的原则是:包含签名,不包含后面。
// 所以,这里的参数是6 ,9. 找到的是678三
个位置的字符。
System.out.println("substr:" + substr);
int begin = str1.indexOf("tom") ;// 截取子字符串的起点
int end = begin + "tom".length() ;
substr = str1.substring(begin , end);
System.out.println("substr:" + substr);
// 3. 练习: alice@qq.com , 把邮箱名找出来。
String email = "alice@qq.com";
int index = email.indexOf('@');
String emailName = email.substring(0 , index);
System.out.println(emailName);
// 4. getBytes :
// ** 字符编码方式: utf-8(3个字节代表一个中文) , gbk(2个字代表一个中文)
String str2 = "中国";
byte[] b1 = str2.getBytes() ;// 表示没有设置用什么字符编码来获取。 就看当
前文件使用的编码方式: utf-8
byte[] b2 = str2.getBytes("utf-8"); // 指定utf-8
byte[] b3 = str2.getBytes("gbk"); // 指定gbk.
System.out.println(Arrays.toString(b1)); //[-28, -72, -83, -27,
-101, -67]
System.out.println(Arrays.toString(b2)); //[-28, -72, -83, -27,
-101, -67]
System.out.println(Arrays.toString(b3)); //[-42, -48, -71, -6]
String str3 = new String(b2);
System.out.println("str3:" + str3);
String str4 = new String(b2 , "gbk"); // gbk编码,两个字节转为一个字符。
System.out.println("str4:" + str4); // str4:涓浗
}
}
正则表达式是一个特殊的字符串,是由一些特定的字符串组成的“规则字符串”。
正则表达式,主要用于对字符串进行匹配,查找,替换等操作。
正则表达式的规则:
可以出现的字符
可以出现的字符简写
字符出现的次数限定
其他规定
split方法把字符串按照指定的分隔符,分割为字符串数组。
String提供了用于字符串替换的方法
参考代码
import java.util.Arrays;
//正则表达式 - regex
// ** ① 能出现哪些字符 , ② 字符出现的次数 , ③ 特殊规定: 比如正则表达式限定开始,和
结尾
public class StringDemo1 {
public static void main(String[] args) {
String str = "aa123bb33cc33333dd1122";// 字符串
// String[] strs = {"aa","bb" ,"cc" ,"dd"}
// 要求: 根据数字进行切割,把连续的字母获取出来,保存在一个数组中。
String regex = "[0-9]{1,}";
String[] split = str.split(regex);
System.out.println(Arrays.toString(split));
// 练习:把字符串转为字符数组,然后用-把这个数组连成一个字符串
String str2 = "123456789" ;// 1-2-3-4-5.......
char[] chars = str2.toCharArray();
String str3 = "";
for(int i=0 ; i < chars.length ; i++){
str3 = str3 + chars[i] + "-" ;
}
// 去掉最后的一个减号
str3 = str3.substring(0 , str3.length()-1);
System.out.println("str3:" + str3);
String[] split1 = str2.split("");
System.out.println(Arrays.toString(split1));
// 练习2: 2023.1.5 , 2023-1-5
String year ="2023.1.5" ;// 转为 2023-1-5
String regex1 = "\\.{1}"; // \\.表示.本身
String[] split2 = year.split(regex1);
String newYear = split2[0]+"-"+split2[1]+"-"+split2[2];
System.out.println(newYear);
// 练习2: 2023.1.5 , 2023-1-5
String s = year.replaceAll("\\.{1,}", "-"); // 根据正则表达式,把匹配成功
的内容,替换为“-”
System.out.println("s:" + s);
// 练习3: “djdtomdddtomxxeretomxxtom” , 要求就是把tom 修改为TOM.
String s1 = "djdtomdddtomxxeretomxxtom" ;// 正则表达式就是“tom”。
String s2 = s1.replaceAll("tom", "TOM");
System.out.println("s2:" + s2);
// 练习四: 敏感词过滤
String s3 = "你最近身体好吗 , 你妈最近身体好吗, 你妹妹身体好吗!";
String s4 = s3.replaceAll("你妈|你妹", "******");
System.out.println("s4:" + s4);
}
}
StringBuilder封装可变的字符串,StringBuilder对象创建后,可以通过调用方法,修改字符串的内容。
StringBuilder构造器函数
public StringBuffer();
public StringBuffer(String str);
StringBuffer提供的操作字符串的方法:append(增加,附加),insert,delete,replace,reverse等
StringBuffer提供的很多方法,返回值是StingBuffer(return this),所以支持链式操作:
StringBuffer sb = new StringBuffer("a");
sb.append("b").append("b").......
// *** 如果要频繁修改字符串的时候,借助于StringBuilder对象,对字符串进行操作,最后再转换为
String类型。
public class StringBuilderDemo1 {
public static void main(String[] args) {
// 1. 测试StringBuilder中的方法
StringBuilder sb = new StringBuilder("abc");
System.out.println(sb.toString()); // 把StringBulder对象的成员变量,转换为字
符串。
String str1 = sb.toString();
System.out.println("str1:" + str1);
// ** append : 追加,一般指在末尾添加。
sb.append("def");// "abcdef" ;
// ** insert :插入 , 一般指定要插入的位置为插入的起点。其它内容后移动。
sb.insert(3 , "ABC");
sb.insert(sb.length() , "DEF") ;// 代表插入在末尾。
System.out.println(sb);
// **replace : 替换 , 把指定位置的字符串替换为其它字符串。
sb.replace(3, 6 , "你好");
System.out.println(sb);
// ** delete: 删除, 把指定位置的字符串给删除掉。
sb.delete(3,6);
System.out.println(sb);
// **reverse: 反转。
sb.reverse() ;
System.out.println(sb);
// 判断一个字符串是否为回文:
String str = "abc";
StringBuilder sb1 = new StringBuilder(str);// sb1中包含“abc”
StringBuilder sb2 = sb1.reverse() ;// 反转 sb1中包含“cba”
// 字符串的内容是否相等:equals方法
/*
if(str.equals(sb1.toString())){ // abe.equals("cba")
System.out.println("是回文");
}else{
System.out.println("不是回文");
}
*/
if(sb1.equals(sb2)){
System.out.println("是回文");
}else{
System.out.println("不是回文");
}
}
}
java.util.Date用于封装日期和时间信息。
Date date = new Date();
date中的日期为系统的当前时间。
重写了toString方法,输出内容为日期
Date类中大多数用于操作时间分量的方法都被Calendar取代
Date中的getTime,setTime方法是用于获取/设置时间毫秒数。
将日期类型,格式化为指定日期表示的字符串。
将符合日期格式的字符串,解析为日期类型。
日期符号:yyyy年,MM月,dd日 时间:HH小时,hh小时,mm分钟,ss秒
// Date :jdk提供的日期的类型
//SimpleDateFormat : jdk提供的日期转换相关的类型。
// *** 创建SimpleDateFormat对象的时候,需要指定日期的格式。 然后后续操作中,就按
此格式写日期。
public class DateDemo {
public static void main(String[] args) throws ParseException {
//1 . 创建日期对象
Date date = new Date(); // date对应的对象中包含的日期为当前系统时间。
// 2. Date中重写了Object的toString方法
System.out.println(date); // Fri Jan 06 14:07:02 CST 2023
// 3. 获取时间毫秒数
long time = date.getTime();
System.out.println("time:" + time);
// 4. 设置时间毫秒
date.setTime(365L*24*60*60*1000); // 1971.1.1
System.out.println(date);
// 设置时间为你的生日: 2002.10.5 *** Date类型就不方便做这个, 后期使用Calendar类
型。
// 5. 日期格式化:SimpleDateFormat ,日期格式的转化
// ** yyyy 代表四位年 , MM 代表2位表月 , dd代表2位日
// HH 24小时,hh 12小时 , mm 分钟 , ss代表秒 ,
// yyyy-MM-dd HH:mm:ss -- 常用的日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //
yMdHms都会替换为具体的日期相关的数字
// format: 格式化,把Date类型转换为String类型。
String format = sdf.format(date);
System.out.println(format);
// parse :解析 , 把日期格式的字符串,转换为Date类型。
// String birth = "2002.10.5 15:30:20"; // java.text.ParseException:
Unparseable date: "2002.10.5 15:30:20"
// String birth = "2002-10-5"; // java.text.ParseException: Unparseable
date: "2002-10-5"
String birth = "2002-10-5 15:30:20";
Date date1 = sdf.parse(birth);//parse ,抛出异常。 解决方法:在调用parse的main
方法上继续抛出异常
System.out.println(date1); // Sat Oct 05 15:30:20 CST 2002
// 练习: 计算到今天为止, 你活了多少天。
birth = "2002-10-5 15:30:20";
Date now = new Date();
Date myBirth = sdf.parse(birth);
// 计算时间毫秒数之差, 然后除以每天的毫秒数,最后就得到你活了多少天了。
long cha = now.getTime() -myBirth.getTime();
long day = cha/(24*60*60*1000);
System.out.println("day:" + day);
sdf = new SimpleDateFormat("yyyy-MM-dd HH:MM:ss"); // 错误: MM代表就是月。
// sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(now));
}
}
java.util.Calendar类,用于封装日历信息,包含了日期,主要用于操作时间分量。
Calendar是抽象类,可以通过其工厂方法获取到其子类的对象。
java.util.GregorianCalendar是Calendar的最主要使用的实现类,其对应世界上大多数国家/地区使用的标准日历
// Calendar :抽象类。日历
public class CalendarDemo {
public static void main(String[] args) {
// 1. 创建对象
Calendar c1 = Calendar.getInstance();//getInstance()是将抽象类实例化
System.out.println("c1:" + c1);
Calendar c2 = new GregorianCalendar();
System.out.println("c2:" + c2);
// 2. 获取时间分量(年,月,日,时分秒,周几.....)
int year = c1.get(Calendar.YEAR); // public final static int YEAR = 1;
// c1.get(1)--获取年; ** 不推荐
int month = c1.get(Calendar.MONTH); // 月: 0 ~11 ,代表1~12月。
int day = c1.get(Calendar.DATE);
int hour = c1.get(Calendar.HOUR_OF_DAY); // HOUR_OF_DAY (24小时制) ,
HOUR(12小时)
int min = c1.get(Calendar.MINUTE);
int second = c1.get(Calendar.SECOND);
int week = c1.get(Calendar.DAY_OF_WEEK); // 周日 周1~周5 周六 : 1~7
// ?: 三目运算
String wk = week == 1? "周日": "周"+(week-1);
System.out.println( year +"-"+ (month+1) +"-"+day +" " + hour +":"+min
+":"+second + " " + wk);
}
}
// Calendar :抽象类。日历
public class CalendarDemo {
public static void main(String[] args) {
// 1. 创建对象
Calendar c2 = new GregorianCalendar();
// 3. 设置时间分量 : 2021-11-11 12:00:00
//c2.set(2021, 10 , 11 , 12 ,00,00);
c2.set(Calendar.YEAR , 2021);
c2.set(Calendar.MONTH , 10);
c2.set(Calendar.DAY_OF_MONTH , 11);
c2.set(Calendar.HOUR_OF_DAY , 12);
System.out.println(c2);
}
}
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
// 通过add方法,实现对世间分量的增加操作。
// ** add: 原有时间上做增加(减少)
// set: 直接设置为某个指定的时间
// get: 获取时间分量的
public class CalendarDemo4 {
public static void main(String[] args) {
// 2021-10-10
Calendar c = new GregorianCalendar(2021,9,10);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// sdf.format(Date) -- 日期格式化
System.out.println("起始时间:" + sdf.format(c.getTime())); // c.getTime
-是Date.
// 10年后的今天
// ** add: 对时间分量进行增加。
c.add(Calendar.YEAR , 10);
System.out.println("10年后的今天:" + sdf.format(c.getTime()));
// 如果能倒回去20年
c.add(Calendar.YEAR , -20);
System.out.println("倒回去的20年:" + sdf.format(c.getTime()));
// 希望15年后,又2个月, 又30天,相聚在母校。
c.add(Calendar.YEAR , 15);
c.add(Calendar.MONTH , 2);
c.add(Calendar.DAY_OF_MONTH , 30);
System.out.println("聚会的日期:" + sdf.format(c.getTime()));
}
// 练习: 提供用户输入出生年,月, 日 都是int类型,然后告诉用户,再过20年,用户多少岁。
Scanner scanner = new Scanner(System.in);
System.out.print("请输入年份:");
int year = scanner.nextInt();
System.out.print("请输入月份:");
int month = scanner.nextInt();
System.out.print("请输入几号:");
int day = scanner.nextInt();
Calendar c1 =Calendar.getInstance();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy");
c.add(Calendar.YEAR,20);
System.out.println("你现在是:" + sdf1.format(c1.getTime()));
}
// Calendar的 getTime方法 : 返回值是Date类型
// Date的getTime方法 :返回值是时间毫秒数, 是long类型
// *** 从Calendar类型,可以得到Date类型。
public class CalendarDemo3 {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance() ;// Instance - 实例化 , 包含的
是当前系统时间。
Date time = calendar.getTime(); // 日期类型: time中包含的日期就是日历中的同一
个日期。
// 获取时间毫秒数
long c1 = calendar.getTimeInMillis() ;
long t1 = time.getTime() ;// 时间毫秒数
System.out.println(c1);
System.out.println(t1);
// 练习:2012-12-8 15:2:20 -- 一个Date中包含的日期
// 使用Calendar来完成: 创建Calendar对象,设置时间分量,然后通过getTime方法,
得到这个Date.
Calendar c11 = Calendar.getInstance() ;
c11.set(2012,11,8 ,15,2,20);
Date d11 = c11.getTime() ;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(d11));
}
}
8种基本数据类型,对应8种包装类。每一个包装类封装了一个对应基本数据类型的成员变量,同时还提供了一些针对该类型操作的方法
1.2自动装箱&自动拆箱
jdk5之后,基本数据类型和包装类的转换,依靠编译器的“预处理”完成。
// 8种基本数据类型,jdk都提供了对应的引用类型, 一般把8种引用类型称为基本类型的包装类。
// ** 如果java没有这8个基本数据类型,那么java就是纯面向对象。
// ** 平时使用的时候,包装类和基本数据类型会自动转换(自动装箱,自动拆箱)
// ** 需要注意包装类可以是null。 基本数据类型有自己的默认值。
public class IntegerDemo {
public static void main(String[] args) {
// 1. 创建对象,
Integer i1 = new Integer(100);
System.out.println(i1); // 100
// NumberFormatException: 数字格式化异常。
// Integer i2 = new Integer("100a"); // java.lang.NumberFormatException:
For input string: "100a"
// System.out.println(i2);
Integer i3 = new Integer("200");
System.out.println(i3);
// 2. 调用方法。
float f1 = i3.floatValue(); // 调用方法,把int 转化为float.
Integer i2 = new Integer(100);
int b = i3.compareTo(i2) ;// 如果i3 > i2 成立, 返回值为正数 。 如果是i3 < i2
,返回值是负数。 否则0.
System.out.println("b:" +b );//b:1
if(b >0){
System.out.println("i3大");
}else if(b<0){
System.out.println("i2大");
}else{
System.out.println("相等");
}
System.out.println(i1.equals(i2)); // true :重写了equals方法
// 3. 类型转换
Integer i4 = 100 ; // 自动装箱
int i = new Integer(100); // 自动拆箱
System.out.println(i4 == i); // true : 因为自动拆箱 , 转换为int类型了。
System.out.println(i4.equals(i)); // true : 自动装箱
// System.out.println(i.equals(i4)); // 编译错误: int类型, 没有方法。
// 4. Integer的其它方法
System.out.println("最大值:" + Integer.MAX_VALUE);
System.out.println("最小值:" + Integer.MIN_VALUE);
String str = "100";
int a = Integer.parseInt(str) ;// 解析为整数
System.out.println(a);
String str1 = "1000.2345";
// ** 有异常
// int a1= Integer.parseInt(str1); // java.lang.NumberFormatException:
For input string: "1000.2345"
// System.out.println(a1);
System.out.println(Integer.toBinaryString(100)); // 转为2进制的字符串:
1100100
System.out.println(Integer.toHexString(100)); // 转为16进制: 64
System.out.println(Integer.reverse(123456));
}
}
java中的浮点数类型(float,double)在运算时会有舍入误差,如果希望得到精确的运算结果,可以使用java.math.BigDecimal类
jdk中提供的整数类型(int,long)的储范围有限,当需要进行很大的整数运算时,可以使用java.math.BigInteger,理论上BigInteger数据范围只受内存容量的限定。
BigInteger提供了:add,multiply,subtract,divide等方法
jak中提供了将对象存储于特定数据结构的容器,被称为集合
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
// Collection:集合 , 是一个interface , 定义集合操作的通用方法。
public class CollectionDemo {
public static void main(String[] args) {
// 1. 创建对象
Collection c = new ArrayList(); // 创建了集合的对象 , 没有限定集合的元素类型。
// Object[] objs =new Object[5] ;// 说明数组元素类型不限
System.out.println("size:" + c.size()); // 0
System.out.println("是否为空:" + c.isEmpty()); // true .
// 2. 给对象添加元素
c.add(new String("hello"));
c.add(100); // 100会自动装箱为Integer
c.add(3.5); // 3.5会自动装箱为Double.
c.add(new Date()) ;
c.add(new Boolean("true"));
System.out.println("size:" + c.size()); // 5
System.out.println("是否为空:" + c.isEmpty()); // false
// 3. Collection中的其它方法
String str1 = new String("hello");
boolean b = c.contains(str1);
System.out.println(b); // true: 说明集合c中,包含了str1. 因为集合中判断是否包
含,是以equals的结果为准。
c.addAll(c); // 集合中批量添加元素。
System.out.println("size:" + c.size()); // 10 :
// c.clear(); // 清除元素
// System.out.println("size:" + c.size()); //0
c.remove(3.5) ;// 删除一个
System.out.println(c);
// c.removeAll(c); // 批量删除
System.out.println(c);
// 4. 集合的迭代: Iterator
System.out.println("----------------------");
Iterator iterator = c.iterator();
// *** 必须先调用hasNext方法, 然后才能调用next方法。
// iterator.hasNext() :判断有没有下一个元素,有返回值为true ,没有返回值为false
// iterator.next() : 获取下一个元素 ,返回元素的值。
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
List接口的实现类实现了Collection接口的方法
重写了父类的方法
List接口中根据线性表的数据结构定义了一些基于下标操作的方法。
get & set :
get(i)
set(i, obj)
add & remove
add(i ,obj)
remove(i)
public class ListDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
System.out.println("size:" + list.size()); // Collection中定义的方法
System.out.println("是否为空:" + list.isEmpty());
// 1. 添加元素 : 每个元素的索引位置由按添加顺序决定。
list.add(new Student("1001" , "alice" , 20));// index 是0
list.add(new Student("1002" , "tom" , 30));// index 是1
list.add(new Student("1003" , "jack" , 21));// index 是2
list.add(new Student("1004" , "rose" , 19));// index 是3
// 2. 根据元素的位置,找到元素. : 遍历集合
// get(index) - 获取索引位置的元素
for(int i =0 ;i < list.size() ; i++){
System.out.println(list.get(i));
}
// 3. 修改集合的元素
// set(i , object) - 设置索引位置的元素
list.set(1 , new Student("1002" , "lilei" , 20));
// 4. 在指定的位置,增加一个元素。
// add(index , object) -- 在index位置,添加一个元素,该位置元素,依次后移
动。
list.add(1 , new Student("1005" , "hanmeimei" , 18));
System.out.println(list);
// 5. 删除指定位置的元素
// remove(index) - 删除index位置的元素。
list.remove(0);
System.out.println(list);
// 练习: 1005这个学生和最后一个学生1004交换位置。
Student temp = list.get(0) ;//
list.set(0 ,list.get(list.size()-1));
list.set(list.size()-1 , temp);
System.out.println("最后的list");
System.out.println(list);
// 练习: 使用迭代器,遍历list 。
System.out.println("迭代器遍历");
final Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
List接口的两个实现类分别用ArrayList,LinkedList
ArrayList用动态数组实现,适合于随机访问
LinkedList用链表实习,适合于插入和删除
ArrayList和LinkedList的方法一样,但是性能不同
public class ListDemo1 {
public static void main(String[] args) {
// 1. 创建一个ArrayList类对象, 集合中存放的元素是Point.
ArrayListList list = new ArrayList<>();
// 2. 添加3个Point在集合中
Point p1 = new Point(1,2);
Point p2 = new Point(2,3);
Point p3 = new Point(3,4);
list.add(p1);
list.add(p2);
list.add(p3);
// 3. 获取到第二个元素,修改x , y的值
Point p = list.get(1);
p.setX(10);
p.setY(10);
// 4. 在集合的第一个位置,添加一个Point
Point p4 = new Point(4,5);
list.add(0 , p4);
// 5. for循环变量集合,修改元素的x为x+i
for(int i = 0; i < list.size() ; i ++){
Point pp = list.get(i);
// pp.setY(pp.getY() + i);
pp.setX(pp.getX() + i);
}
// 6. 删除第三个元素
list.remove(2);
// 7. 输出集合的所有元素
System.out.println(list);
// 8. 清空集合中的元素
list.clear();
}
}
所有Collection的实现类,都实现了iterator方法,该方法返回一个iterator接口类型的对象,用于
对集合中的元素进行迭代遍历。
Iterator中有三个方法:
boolean hasNext();//判断是否还有下一个元素
E next();//取出下一个元素
default void remove();//在原集合中返回
泛型是java1.5引入的新特性,泛型的本质是参数类型化。在类,接口和方法的过程中,所操作的数据类型被传入的参数指定。
java泛型机制广泛低应用于集合中,所有集合集合类型都带有泛型,在创建集合对象的时候,指定集合中元素的类型。
java编译器根据泛型的类型,对集合中元素进行类型检查,减少运行的时候错误
jdk1.5中,推出了增强型for循环的语法,适用于遍历数组和集合中的元素
package seday4;
import java.util.ArrayList;
import java.util.List;
// 增加for循环: for(类型 变量名:集合){ 使用变量}
public class ForDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add('A');
list.add('B');
list.add('C');
list.add('D');
// 增强for循环 : 集合遍历的时候,简化for循环。
for(Character c : list){
System.out.println(c); // c代表的就是集合种的当前被遍历的元素。
}
String[] strs = {"acb" ,"def" , "hij" , "mnk"};
for(String str: strs){
System.out.println(str);
}
}
}
List的subList方法用于获取子list
subList获取的子list和原来的list占有相同的存储空间,对subList进行操作会影响原来的List
import java.util.ArrayList;
import java.util.List;
// ** subList找子集合: 子集合和原集合共用存储空间, 子集合或者父集合内容修改,子集合和父集合
的内容都被该了。
public class ListDemo {
public static void main(String[] args) {
List list =new ArrayList() ;
// 给list元素赋值为0 ~ 9
for(int i = 0; i < 10 ; i ++){ // 0,1,2,3,4,5,6,7,8,9
list.add(i);
}
// 截取子集合
List integers = list.subList(4, 8);// 4,5,6,7
System.out.println(integers);
// 练习: 把integers种的元素扩大10倍 . 然后输出list.
// ** 使用增强for循环
int index = 0 ;
for(Integer i : integers){
//i *=10 ;
integers.set(index ++ , i * 10 ); // index ++ : 先使用index的数据值,
然后增加1. 方便下次使用。
}
System.out.println(integers);
System.out.println(list);
Integer i=1;
}
}
队列是常用的数据结构,可以将队列堪称是特殊的线性表,队列只能从一端添加元素(offer),另一端取出元素(poll)
队列遵循先进先出的原则(FIFO,first in first out)
Queue是jdk中的接口,LinkedList是Queue的实现类
Queue接口中的主要方法:
package seday4;
import java.util.LinkedList;
import java.util.Queue;
// Queue : 队列, 先进先出
/*
public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable
public interface Deque extends Queue
*/
public class QueueDemo {
public static void main(String[] args) {
Queue q ;
q = new LinkedList();
// 入队操作
q.offer("小A");
q.offer("小B");
q.offer("小C");
q.offer("小D");
// 查看排在队首的元素
System.out.println("队首:" + q.peek());
// 出队操作
System.out.println("———————————————出队—————————————————");
System.out.println(q.poll()); // 小A
System.out.println(q.poll()); // 小B
System.out.println(q.poll()); // 小C
// 入队一个
q.offer("小E");
System.out.println("队首:" + q.peek()); // 小D.
// 继续出队
System.out.println(q.poll());
System.out.println(q.poll()); // 队列中的最后一个元素,出队了。
// q.poll(); 队列没有元素了,返回null.
}
}
Deque是Queue的子接口,被称为双端队列,即可以从队列的两端入队(offer),出队(poll),LinkedList实现了该接口。
将Deque限定为只能从一段出队和入队,就可以模拟栈的数据结构,栈数据结构,入栈push,出栈pop.
栈遵循先进后出的原则FILO(first in last out)
// Deque : 双端队列, 可以从前进, 出, 也可以从后进,出。
// ** 栈数据结构: 先进后出 。 把双端队列限定为只能一边进出,模拟栈数据结构。
// 栈: 先进后出。
public class DequeDemo {
public static void main(String[] args) {
Deque deque = new LinkedList<>();
// 入栈操作
deque.push("小A");
deque.push("小B");
deque.push("小C");
deque.push("小D");
System.out.println("最先出栈的:" + deque.peek());
// 出栈操作
System.out.println(deque.pop()); // 小D
System.out.println(deque.pop()); // 小C
System.out.println(deque.pop()); // 小B
System.out.println("即将要出栈的:" + deque.peek());
// 入栈
deque.push("小E");
deque.push("小F");
System.out.println("最先出栈的:" + deque.peek()); // 小F
System.out.println(deque.pop()); // 小F
System.out.println(deque.pop()); // 小E
System.out.println(deque.pop()); // 小A
}
}
Comparable接口
针对对象数组或者集合中的元素进行排序的时候,首先需要确定元素的比较逻辑,制定比较的比较规则。
Jdk中的Comparable接口,定义了对象间大小比较方法,需要大小比较的类,可以实现该接口。
public int compareTo(T o);
返回正数: this > o
返回负数: this < o
返回0 : this == o
Comparable接口的实现
Collection类中提供了一些对集合操作的方法,其中比较常用的有对List的排序方法。
如果要使用Collection.sort()方法进行对集合中的元素排序,那么要求集合中的元素是实现类
Comparable接口的,即对象可以比较大小。
Comparator接口的实现
参考代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
//Collections : 操作 集合的工具类
// Collections.sort(集合)
// Collections.sort(集合,Comparator )
public class CollectionsDemo {
public static void main(String[] args) {
// 给集合中的String排序: 默认按字母顺序
List list = new ArrayList<>( );
list.add("one");
list.add("two");
list.add("three");
list.add("four");
Collections.sort(list);
System.out.println(list);
// 给学生排序: 按学生的年龄
List stus = new ArrayList<>();
stus.add(new Student("10" , "tom" , 10));
stus.add(new Student("10" , "alice" , 9));
stus.add(new Student("10" , "jack" , 13));
stus.add(new Student("10" , "lilei" , 8));
stus.add(new Student("10" , "haimeimei" , 12));
Collections.sort(stus);
System.out.println("学生们:");
System.out.println(stus);
// 练习: 按字符串的长度排序。
// ** 排序的时候,制定排序规则: 通过匿名内部类,重写compare方法,让
Collections.sort按我们指定的compare方法进行比较。
Collections.sort(list, new Comparator() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
System.out.println(list);
}
}
Set 用于存储不重复的对象集合, set集合中存储的对象,不存在两个对象的equals相等。
HashSet 和 TreeSet是Set集合的两个常用实现类。
HashSet 用hash表实现了Set集合。
TreeSet 用排序二叉树实现了Set集合。
Set集合中的元素,不能和顺序的下标对应,无法从Set集合中获取除特定的元素。
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
// Set : 无序,不重复
// List : 有序, 可重复
public class SetDemo {
public static void main(String[] args) {
// 练习:把你的爱好存储到集合中
Set set = new HashSet<>();
set.add("吃饭");
set.add("睡觉");
set.add("打麻将");
set.add("吃饭"); // 不重复,set中已经存在了。
set.add("吃饭");
System.out.println(set); // [吃饭, 睡觉, 打麻将]
// 练习: 在集合中存储20个不重复的100以内的整数。
Set sets = new HashSet<>();
Random r = new Random();
while (true){
sets.add(r.nextInt(100));
if(sets.size() == 20){
break;
}
}
System.out.println(sets);
// 练习: 随机产生1-36的六个整数。
// 练习: 使用迭代器,变量set。
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
8.MAP
Map集合主要用于存储“key-value”键值对的数据,key可以看作是value的索引,通过key,查找到
value值, key不能重复。
Map是一个interface , 有多种实现类,比如hash表实现的HashMap ,排序二叉树实现的TreeMap
等
HashMap是Map比较常用的实现类。
Map接口中常用的get,put方法
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo {
public static void main(String[] args) {
// 存储数据: 语文- 80, 数学-90 , 英语-65
Map map = new HashMap<>();
map.put("语文" , 80); // key - value
map.put("数学" , 90);
map.put("英语" , 65) ;
System.out.println(map.containsKey("语文")); // true
System.out.println(map.containsValue(90)); // true
Integer integer = map.get("语文");// 根据key , 找value
System.out.println(integer);
System.out.println(map.get("物理")); // 找不到, 返回值是null.
map.put("语文" , 100); // 因为可以已经存在, 所以是在修改value值。
map.remove("语文");// 删除key-value
System.out.println(map.size()); // 2.
Set strings = map.keySet(); // 把键取出来,保存在一个set中。
System.out.println(strings); // [数学, 英语]
Collection values = map.values(); // 把所有的value取出来,存储
在集合中。
System.out.println(values); // [90, 65]
}
}
MAP遍历
for遍历
迭代器遍历
java异常结构中定义有Throwable, Exception 和 Error 是其派生的两个子类。其中Exception表示由于网络故障,文件损坏,设备错误,用户输入非法等情况导致的异常,而Error表示java运行时环境出现的错误,例如jvm内存资源耗尽。
异常出来就是当异常发生的时候妥善地终止程序,避免灾难性后果的发生,具体的操作通常包括;
通知:向用户通知异常的发生。
恢复:保存重要的数据,恢复文件, 回滚事务等
退出:以更好的方式结束程序的运行
try{...}语句包裹了一段代码,该段代码就是捕获异常的范围。
在执行try语句块的过程中,该段代码可能会抛出一种或多种异常,catch语句块可以分别对不同的异常做处理。
如果try语句块没有发生,那么所有的catch语句都被忽略,不被执行。
catch语句块中是对异常处理的代码,不同的异常,可以多个不同的catch语句块。
当由多个catch语句的块的是,应该先捕获小类型的异常,或捕获大类型的异常。
try...catch代码参考
/ exception: 异常
// *** 异常捕获 : try ... catch
// try: 尝试执行这些代码块, 如果有异常, 转到catch语句执行,如果没有异
常,就跳过catch.
public class ExceptionDemo1 {
public static void main(String[] args) {
/* 程序员想的是: 这几行代码,如果执行的是,有异常发生了,就跑出去, 自己不处理。
这里由于是main方法, 抛出去就给jvm了。 然后jvm就把异常的堆栈信
息,打印在控制台。
String str = "abc";
char c = str.charAt(str.length());
System.out.println("c:" + c);
*/
/* try ... catch , 程序员想的是: 如果有异常了,就在catch语句块中的处理。*/
try{
String str = "abc";
//char c = str.charAt(str.length()); // 发生异常了。
char c = str.charAt(str.length()-1); // 根据异常的提示,修改代码。
System.out.println("c:" + c);
}catch (StringIndexOutOfBoundsException e){ // 捕获到这个异常,然后就处
理。
// e.getMessage() : 是异常类中的方法,返回值是异常的提示信息。
//System.out.println(e.getMessage());
System.out.println("不能获取到最后一个字符,因为你的索引位置超出字符串的索
引位置。");
}
}
}
try..catch..catch代码参考
// 异常捕获: try...catch ...catch , 语法规则: 可以try后面写多个catch语句块,把不
同的异常,用不同的处理方式。
// ** try语句块中出现异常,则立即转入catch中执行代码,然后try语句块中异常代
码之后的代码不再被执行。
// 异常种类: 编译时异常 : 必须处理,处理方案: ① try...catch , ② throws 抛出去
// 运行时异常 : 是否程序员决定 ,如果程序没有捕获,就选择抛出去。
// *** 异常是给程序员看的: 根据异常去调试程序,解决程序的bug。
public class ExceptionDemo2 {
public static void main(String[] args) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// sdf.parse("abc"); // 异常.... ParseException
sdf.parse("2023-1-11");// 根据异常提示,修改为正确的代码。
int k = 0;
int m = 10 / k; // 异常。。。ArithmeticException
String str = null;
str.length();// 异常....
} catch (ParseException e){
System.out.println("只能yyyy-MM-dd格式的字符串,才能解析为日期类型。");
}catch (ArithmeticException e){
System.out.println("请检查除数, 除法运算,除数不能为0.");
}catch (NullPointerException e){
System.out.println("空指针异常。null没有方法和属性。");
}catch (Exception e){
e.printStackTrace();
}
// 向上造型: 子类对象赋值给父类的引用。
try{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// sdf.parse("abc"); // 异常.... ParseException
sdf.parse("2023-1-11") ;// 根据异常提示,修改为正确的代码。
int k = 0;
int m = 10/k ; // 异常。。。ArithmeticException
String str = null;
str.length();// 异常....
}catch (Exception e) { // catch的是所以异常的父类。因此try中的任意异常,都
会被诶这个catch捕获。
System.out.println("糟糕,遇到异常了,请检查代码:" +
e.getMessage());
e.printStackTrace(); // 输出异常的堆栈信息。
}
}
}
当程序发生异常,不能处理的时候,会抛出对应的异常。
可以通过throw 语句 ,将异常抛出,那么被抛出的异常,可以让其他代码来捕获这个异常。如果需要自行抛出异常,需要使用“throw”关键字,并且要创建出抛出的异常类的对象。
throw new XXException();
类中有很多方法,当在类中的方法中使用到了throw 语句抛出了异常,那么在这些方法的声明过程中,需要使用throws 关键字,声明该方法抛出的异常。
public void add() thows XXExcedption{
// code
}
如果在方面声明的时候,使用throws抛出了异常,那么在调用方法的时候,就需要捕获异常,或者
继续抛出异常。
代码参考
// throw: 抛 ,一次只能抛一个异常, 用于在方法内部抛出异常。 throw new
XxException();
// throws : 抛 , 用于在方法声明上, 支持一个方法抛出多个异常。
//public void 方法名() throws xx ,yy { }
public class ExceptionDemo3 {
public static void main(String[] args) {
try {
age(20);
} catch (Exception e) {
e.printStackTrace();
}
throw new NullPointerException();// 抛出一个异常。
}
// 练习: 定义一个方法,判断用户的年龄,如果用户的年龄大于120 , 小于0 ,则抛出异常。
// **age方法内部通过throw 抛出了异常。 要求程序必须解决这个问题:
// ① 使用try...catch , ② 在方法声明上使用throws 声明抛出异常
// **定义方法的时候,如果内部使用throw抛出异常,那么一般就会在方法声明上,
// 使用throws抛出这个异常。让调用方法的人,决定怎么处理这个异常。
/*
public static void age(int age) throws RuntimeException{} .抛出的是运行时
异常,所以调用方法的时候,就要求处理异常。
public static void age(int age) throws Exception{} ,抛出的是Exception的时
候,调用方法,就会要求要处理异常。
**Exception 包含:编译时异常 和 运行时异常
*/
public static void age(int age) throws Exception{
if(age > 120 || age < 0 ){
// 异常
// throw new Exception("年龄必须在0~120之内。");
throw new RuntimeException("年龄必须在0~120之内。");
}
try{
int k = 0 ;
int m = 10/ k;
}catch (ArithmeticException e){
throw e ; // 抛出异常
}
System.out.println("年龄:" + age);
}
}
finally语句为异常处理提供一个统一的出口,使得在控制流程转到其他程序其他部分之前,能够对程序的状态做统一管理。
无论try语句块中是否有异常,finally语句块的内容都会被执行。
通常在finally语句块中完成一些必须要执行的代码。比如: 文件的关闭,数据库的关闭等。
代码参考:
/ try ...catch ... finally
// ** finally : 有没有异常,最终都要被执行的的代码就放在finally中。
// ** return : finally语句块之前的return ,不能阻止finally中的代码运
行。
// ** System.exit(0); finally语句块之前的System.exit(0) ,能阻止
finally中的代码运行。
public class ExceptionDemo4 {
public static void main(String[] args) {
try{
int k = 0 ;
int m = 10/5 ;
}catch (Exception e){
System.out.println(e.getMessage());
}finally {
System.out.println("程序结束了。");
}
// 笔试题:
int a = data();
System.out.println("a=" + a); // 12
}
public static int data(){
int a = 10 ;
try{
int k = 1 ;
int m = 10/ k;
a++;
return a ;
}catch (ArithmeticException e){
a ++ ;
return a;
}finally {
a++;
return a ;
}
}
程序员可以自定义异常,解决自己程序中的一些特定内容:
public class GenderErrorException extends Exception{// 重写异常类的方法
}
代码参考
自定义异常类:
// 类 继承了Exception, 说明这个类是一个异常类型。
public class GenderException extends Exception {
// ** 定义构造函数
public GenderException(){
super();
}
public GenderException(String str){
super(str); // 调用父类的有一个参数的构造函数。
}
// ** 重写一些方法
@Override
public String getMessage() {
return "性别错误。性别只能是男生或女生。" ;//super.getMessage();
}
@Override
public void printStackTrace() {
super.printStackTrace(); // 调用父类的方法
}
}
自定义异常类的使用:
public class ExceptionDemo5 {
private String sex ;
public static void main(String[] args) {
ExceptionDemo5 ed5 = new ExceptionDemo5();
try {
ed5.setSex("女");
}catch (GenderException e){
e.printStackTrace();
}
}
public void setSex(String sex) throws GenderException{
if(sex.equals("男生") || sex.equals("女生") ){
this.sex = sex ;
}else{
throw new GenderException(); // 如果性别不是“男生”或“女生” ,抛出异常。
}
}
}
程序运行过程中,由某些特定操作引发的异常,被称为运行时异常。
如果父类的方法抛出了异常,那么子类重写父类的时候,异常可以如下处理:
a.不处理异常(重写的时候,不抛出异常)
b.可以只抛出部分异常
c.可以抛出异常的子异常
d. 抛出其他运行时异常
但是不可以如下操作:
a.重写的时候抛出父类没有抛的非运行时异常。
b.重写的时候抛出父类没有抛的Exception。
代码参考
public class ExceptionDemo6 {
}
class Aoo{
public void add() throws ParseException {
}
}
class Boo extends Aoo{
// 重写父类的方法。
/*
正确:父类方法抛出异常,子类方法重写的时候可以选择不抛异常。
public void add(){
}
*/
/*
编译错误: 子类的方法抛出的异常 是父类方法抛出的异常的父类。所以不可以。
public void add() throws Exception{
}
*/
// 正确: 和父类的方法保持完全一致。
// public void add() throws ArithmeticException{
//
// }
// ** 子类重写的时候,如果父类没有抛出Exception,那么子类不能抛出Exception
// public void add() throws Exception{
//
// }
}
java.io.File用于表示文件(目录),程序员可以通过File类在程序中操作硬盘上的文件或目录。
File类只用于表示文件(目录)的信息(大小,名称等),不能对文件内容进行访问
参考代码
// File: java.io.File -- jdk中提供的用于表示硬盘上文件或目录的类型。
public class FileDemo {
public static void main(String[] args) throws IOException {
// 创建对象: File(文件路径)
File file = new
File("C:\\Users\\Administrator\\Desktop\\hello.txt");
final boolean b = file.canExecute();
System.out.println(b); // true
final String absolutePath = file.getAbsolutePath();
System.out.println(absolutePath); // 绝对路径:
C:\Users\Administrator\Desktop\hello.txt
final long length = file.length(); // 获取字节数
System.out.println(length); // 38 字节。
final String name = file.getName();
System.out.println(name); // hello.txt
// 练习: 找出后缀名。
final String substr = name.substring(name.indexOf(".") + 1,
name.length());
System.out.println(substr);
final boolean file1 = file.isFile(); // 判断是否是文件
if(file1){
System.out.println("是一个文件");
}else{
System.out.println("是一个目录");
}
File file0 = new
File("C:\\Users\\Administrator\\Desktop\\hello1.txt");
final boolean exists = file0.exists();
if(exists){
System.out.println("存在");
}else{
System.out.println("不存在");
// createNewFile -- 创建新文件。
file0.createNewFile() ;//
}
// //file0.mkdir() - 创建目录
// 练习: 在桌面上创建一个文件夹名字叫abc , 然后在abc中创建一个文件,名字叫
def.txt.
File abc = new File("C:\\Users\\Administrator\\Desktop\\abc");
if(!abc.exists()){
abc.mkdir() ;// 创建目录
}
File def = new
File("C:\\Users\\Administrator\\Desktop\\abc\\def.txt");
if(!def.exists()){
def.createNewFile();
}
}
}
节点流: 从特定的地方读写的流,例如磁盘或一块内存区域。
FileInputStream : 输入流,操作的是字节,又叫字节流。
FileOutputStrean: 输出流, 操作的是字节,又叫字节流。
参考代码
按字节读取
// ** FileInputStream - 文件输入流
// *** 按字节读取
public class FileInputStreamDemo {
public static void main(String[] args){
FileInputStream in = null;
try {
in = new
FileInputStream("C:\\Users\\Administrator\\Desktop\\hello.txt");
int read = in.read(); // 读取到一个字节
System.out.println(read);
read = in.read();
System.out.println(read);
read = in.read();
System.out.println(read);
byte[] bs = new byte[3];
int read1 = in.read(bs); // 读取的字节内容,保存在字节数组bs中
System.out.println("read1:" + read1);
String str = new String(bs);
System.out.println("str:" + str);
bs = new byte[100];
final int read2 = in.read(bs);
byte[] bs2 = new byte[read2];
System.arraycopy(bs , 0 , bs2 , 0 , read2); // 复制数组的内容。
str = new String(bs2);
System.out.println("str:" + str);
System.out.println("read2:" + read2);
final int read3 = in.read();
System.out.println(read3); //-1 , EOF , 说明读取到文件末尾。
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close(); // 关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
按字节写
// ** FileOutputStream , 文件输出流。
// ** 按字节的方式,把内容写入到文件中。
public class FileOutputStreamDemo {
// public class FileNotFoundException extends IOException
public static void main(String[] args) throws IOException {
//FileOutputStream out = new
FileOutputStream("C:\\Users\\Administrator\\Desktop\\hello.txt"); // 从文件的开始位
置写入数据。
FileOutputStream out = new
FileOutputStream("C:\\Users\\Administrator\\Desktop\\hello.txt" , true); // 从文件
的末尾开始写入数据。
String str = "疑是地上霜。";
out.write(str.getBytes()); // 把字符串转换为字节数组,然后写入到文件中。
out.write(str.getBytes() , 0 , 6); // 从字节数组的指定位置开始找字节,然后找指
定长度的字节内容,写入文件中。
out.write(45);
out.write(65);
out.write(96);
out.close();
}
}
// 练习:读取文件中的所有内容。
文件复制
public class IOUtil {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\Administrator\\Desktop\\hello.txt");
copy(file);
}
// 用户调用copy方法的时候,赋值用户传入的文件
public static void copy(File file) throws IOException {
FileInputStream in = new FileInputStream(file) ;// 读这个文件
FileOutputStream out = new
FileOutputStream("C:\\Users\\Administrator\\Desktop\\abc.txt") ;// 在文件中写入内容
/*
while(true){
int read = in.read();
if(read == -1){
break;
}
out.write(read);
}*/
int read = 0;
while( (read = in.read()) !=-1){ // 读取内容,然后判断是否为末尾。
out.write(read); // 写入内容
}
in.close();
out.close();
}
}
使用节点流作为输入或输出。过滤流是使用一个已经存在的输入流或输出流进行创建的。
BufferedInputStream & BufferedOutputStream 是过滤流
输入流
// ** BufferedInputStream : 带缓冲区的输入流 , 读取效率高于FileInputStream
public class BufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\Administrator\\Desktop\\abc.txt");
FileInputStream inputStream = new FileInputStream(file) ;
BufferedInputStream bin = new BufferedInputStream(inputStream);
/*
byte[] bs = new byte[1024] ;// 一次读取1k.
final int read = bin.read(bs);
System.out.println("读取的内容:" + new String(bs));
System.out.println("read:" + read);
*/
// 读取abc.txt的所有内容。
byte[] bs = new byte[99] ;//
while (true){
int read = bin.read(bs);
if(read < bs.length){ // 读到最后的内容了。
byte[] last = new byte[read];
System.arraycopy(bs , 0 , last , 0 , read);
System.out.println(new String(bs));
break;
}else{
System.out.println(new String(bs)); // 把读取到的内容,转换为字符
串。
}
}
inputStream.close();
bin.close();
}
}
输出流
public class BufferedOutputStreamDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new
FileOutputStream("C:\\Users\\Administrator\\Desktop\\abc.txt",true);
BufferedOutputStream bos =new BufferedOutputStream(fos);
String str = new String("dkldjkldjkldXXXXX");
bos.write(str.getBytes()); // 写入缓冲区
bos.flush(); // 刷新,把内容真的写入到文件中
bos.close(); // ** 关闭的时候,会先把缓冲区的数据,写入到文件中,然后在关闭
流。
fos.close();
}
}
DataOutputStream & DataInputStream是对流功能的扩展,可以更方便的读取int ,long ,char等
类型的数据
DataOutputStream对基本的输出流功能进行扩展,提供了基本数据类型的输出方法
DataInputStream对基本的输入流功能的扩展,它提供了基本类型的输入方法
参考代码
// ** DataOutputStream
// ** DataInputStream
import java.io.*;
public class DataOutputStreamDemo {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\Administrator\\Desktop\\1.txt");
/*
FileOutputStream fos = new FileOutputStream(file);
DataOutputStream dos = new DataOutputStream(fos) ;
dos.write(111);
dos.writeInt(20);
dos.writeChar('A');
dos.writeChars("nice to meet you.");
//dos.flush();
dos.close();
fos.close();
*/
FileInputStream fis = new FileInputStream(file);
DataInputStream dis =new DataInputStream(fis);
final int read = dis.read();
System.out.println("read:" + read);
System.out.println(dis.readInt());
System.out.println(dis.readChar());
byte[] bs = new byte[40];
dis.read(bs);
System.out.println(new String(bs));
dis.close();
fis.close();
}
}
提供字符的处理,一次处理一个字符(unicode)
字符的底层仍然是基本的字节流
InputStreamReader : 完成byte流解析为char流,按照编码解析。
OutputStreamWriter: 提供char流到字节,按照编码处理。
参考代码
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
/*
FileOutputStream fos = new
FileOutputStream("C:\\Users\\Administrator\\Desktop\\2.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos);
osw.write("床前明月光,\n");
osw.write("疑似地上霜.\n");
osw.write("作者...李白");
osw.flush();
osw.close();
fos.close();
*/
FileInputStream fis = new
FileInputStream("C:\\Users\\Administrator\\Desktop\\2.txt");
InputStreamReader reader = new InputStreamReader(fis);
char[] c = new char[6];
reader.read(c);
System.out.println(new String(c));
reader.close();
fis.close();
}
}
BufferReader & BufferWriter更方便处理字符
将Object转换为byte序列,就是序列化,反之将byte序列转换为Object就是反序列化。
对象序列化的目的,是为了将对象,以byte流的方式存储。
使用writeObject(Object)/readObject()进行序列化和反序列化
ObjectInputStream ois ;
ois.readObject();
ObjectOutputStream oos ;
oos.writeObject(obj);
参考代码
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
// ObjectInputStream ois = new ObjectInputStream()
/*
FileOutputStream fos = new
FileOutputStream("C:\\Users\\Administrator\\Desktop\\3.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos) ;
String str = "abc";
oos.writeObject(str); // 写入一个对象。
Integer i = 100;
oos.writeObject(i);
Double d= new Double("10.5");
oos.writeObject(d);
oos.flush();
oos.close();
fos.close();
*/
FileInputStream fis = new
FileInputStream("C:\\Users\\Administrator\\Desktop\\3.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
if(o.getClass() == String.class){
String str = (String) o;
System.out.println("str:" + str);
}
//final int i = ois.readInt();
System.out.println( ois.readObject());
System.out.println(ois.readObject());
ois.close();
fis.close();
}
}
Serializable接口
Serializable是序列化接口,对象必须实现序列化接口,才能进行序列化,否则抛出异常。
java的API中的类,大部分都实现了Serializable接口。
参考代码
// ** 类实现了implements Serializable 这个接口,就可以支持被序列化。
public class Student implements Serializable {
private String stuName;
// ** transient,透明 , 用这个关键字修饰的成员变量,不会被序列化到文件夹中。
private transient int sutAge;
private String stuSex;
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getSutAge() {
return sutAge;
}
public void setSutAge(int sutAge) {
this.sutAge = sutAge;
}
public String getStuSex() {
return stuSex;
}
public void setStuSex(String stuSex) {
this.stuSex = stuSex;
}
}
transient关键字
transient关键字修饰的成员变量,不会被序列化。
private transient Integer brId;
参考代码
package seday6;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
// 把自定义的类型,序列化到文件中
public class ObjectInputStreamDemo2 {
public static void main(String[] args) throws Exception{
FileOutputStream fos = new
FileOutputStream("C:\\Users\\Administrator\\Desktop\\4.txt") ;
ObjectOutputStream oos =new ObjectOutputStream(fos);
Student stu = new Student();
stu.setStuName("alice");
stu.setStuSex("女");
stu.setSutAge(18);
oos.writeObject(stu); //写入对象
oos.close();
fos.close();
// 反序列化,找到Student对象。
FileInputStream fis = new
FileInputStream("C:\\Users\\Administrator\\Desktop\\4.txt") ;
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
if(o instanceof Student){
Student o1 = (Student)o;
System.out.println(o1.getStuName() +":" + o1.getStuSex() +":" +
o1.getSutAge());
}
ois.close();
fis.close();
}
}
java程序支持多线程,并且java中的多线程处理较为简单。 一般操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序被称为进程, 当也给程序运行时候,内部可能包含多个顺序流执行,每个顺序执行流就是一个线程。
Thread类代表线程类型
任何线程对象都是Thread类(子类)的实例
Thread类是线程的模板(封装了复杂的线程开启等操作,封装了操作系统的差异性等)
只要重写run方法,就可以实现具体线程。
创建线程实例(即子类的对象)通过start方法,启动线程,线程启动之后,会尽快执行run方法
package seday7;
public class TestThread {
public static void main(String[] args) throws Exception{
// 一个人负责问1000次你吃了吗, 另一个人回答1000次我吃了。
Thread t = new AskThread() ; // 创建对象
t.start(); // 启动线程
Thread t2 = new AnswerThread();// 创建对象
t2.start(); // 启动线程
}
}
class AskThread extends Thread{ // 继承Thread类型, 重写run方法。
@Override
public void run() {
for(int i = 0 ; i < 1000 ; i ++){
try {
Thread.sleep(100); // 每次循环等待100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("你吃了吗");
}
}
}
class AnswerThread extends Thread{
@Override
public void run() {
for(int i =0 ; i< 1000 ; i ++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我吃了");
}
}
}
创建一个类,实现Runnable接口, 重写run 方法
以实现了Runnable接口的类的实例,作为创建Thread类的参数。
package seday7;
public class TestThread1 {
public static void main(String[] args) {
//Thread thread = new Thread() ;// Thread是线程类。
Thread t = new Thread(new B());
t.start(); // 启动线程。
}
}
class A {
}
class B extends A implements Runnable{
@Override
public void run() {
System.out.println("hello. 吃了吗");
}
}
1.3使用匿名内部类创建线程
public class TestThread2 {
public static void main(String[] args) {
Thread t1 = new Thread(){
@Override
public void run() {
for(int i = 0 ; i < 1000 ; i++){
// try {
// // Thread.sleep(50);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("在选衣服" +i);
}
}
};
Thread t2= new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0 ; i < 1000 ; i++){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("在试衣服" +i);
}
}
});
t1.start();
t2.start();
t1.setPriority(5); // 改变优先级
t2.setPriority(10);
}
}
Thread.sleep(times) 使当前线程从running放弃处理器,进入block状态,休眠times毫秒,在进入
runnable状态。
守护线程是指,在线程运行前,将线程设置为守护线程,那么当程序中没有其他线程运行时候,
jvm退出,程序就终止。
public class TestThread3 {
public static void main(String[] args) {
Thread t1 = new Thread(){
@Override
public void run() {
for(int i =0 ; i < 100 ; i++){
try {
sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello ....." + i);
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
for(int i =0 ; i < 1000 ; i++){
try {
sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hi ....." + i);
}
}
};
t1.start();
t2.setDaemon(true); // 设置t2的线程为守护线程。
t2.start(); // 先设置为守护线程,然后才能启动线程。
}
}
1.7线程的信息
// 线程相关的方法
public class TestThread4 {
public static void main(String[] args) {
// 获取当前线程。
Thread t = Thread.currentThread() ;// 获取当前线程。
System.out.println(t); // Thread[main,5,main]
// 线程id
System.out.println(t.getId());
// 线程名字
System.out.println(t.getName());
// 线程的优先级
System.out.println(t.getPriority());
// state
System.out.println(t.getState());
// alive - 活着
System.out.println(t.isAlive());
// ** 是否为守护线程
System.out.println(t.isDaemon());
}
}
所谓同步,就是指在调用某个功能的时候,在没有得到结果之前,该调用不返回,同时其他线程无法使用这个功能。
异步: 并发,线程间互不影响,自己干自己的。
同步: 步调一致的处理,有序地执行。
多个线程并发读写同一个临界资源的时候,会发生“线程并发安全问题”,使用同步代码块,解决并
发安全问题
synchronized可以修饰方法,表示整个方法修的全部内容需要同步。
synchronized(同步监视器){ // .... } ,同步监视器一般是一个对象。
public class SynchronizedDemo {
public static void main(String[] args) {
SynchronizedDemo sd = new SynchronizedDemo();
Thread t1 = new Thread(new Runnable() {
@Override
public void run(){
try {
sd.add("小红");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run(){
try {
sd.add("小明");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run(){
try {
sd.add("小黄");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
t3.start();
}
public synchronized void add( String name) throws InterruptedException
{
System.out.println("hello:" + name);
Thread.sleep(5000);
}
public void add1(){
System.out.println("hello");
synchronized (this){
for(int i = 0 ; i< 100 ; i++){
System.out.println(i+"----------------------");
}
}
}
}
public class SynchronizedDemo1 {
public static void main(String[] args) {
// SingleA a1 = SingleA.getInstance();
// SingleA a2 = SingleA.getInstance();
// System.out.println(a1);
// System.out.println(a2);
// System.out.println(a1 == a2); // == 判断两个引用指代的对象的地址是否相同。
SingleB b1 = SingleB.getInstance();
SingleB b2 = SingleB.getInstance();
System.out.println(b1 == b2);
}
}
// 单例模式-- 指一个类只能有一个实例,就被称为单例。
class SingleA{
private static SingleA sa = null;
// ① 私有化构造函数 , 避免程序自己创建对象。
private SingleA(){
}
// ② 提供一个公开的方法,由这个方法,返回实例。
// ** synchronized 把这个方法设置为同步的, 那么同一时间只能被一个线程调用到。
public static synchronized SingleA getInstance(){
if(sa == null){
sa = new SingleA(); // 方法同步之后,保重这句代码只会被执行一次。
}
return sa;
}
}
class SingleB{
private static SingleB sb = new SingleB();
private SingleB(){}
public static SingleB getInstance(){
return sb;
}
}
在C/S模式下,客户向服务器端发出请求,服务器端接收到请求之后,提供相应的服务。
客户端部分: 每个用户都有自己的客户端,负责发送请求。
服务端部分: 多个用户共享一个服务器,处理每个客户端的请求。
服务器端:
package seday7;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/*
服务端程序:
-- 启动之后,等待客户端来连接。
-- 一定要先启动服务端,然后再启动客户端。
*/
public class Server {
// 运行在服务器端的socket
private ServerSocket server;
// 构造方法: 初始化server变量
public Server() throws IOException {
System.out.println("开始初始化了......");
server = new ServerSocket(9999);
System.out.println("初始化成功了......");
}
// 启动服务器
public void start(){
while (true){
System.out.println("等待客户端的连接.....");
try {
Socket socket = server.accept() ;// 阻塞方法。
InetAddress inetAddress = socket.getInetAddress();
String ip = inetAddress.getHostAddress(); // 客户端的ip.
int port = socket.getPort() ;
System.out.println( ip+":" + port + "连接成功......");
// 流的使用
InputStream inputStream = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(isr);
String word = br.readLine() ;
System.out.println( ip+":" + port + "说:" + word);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
Server server = new Server() ;
server.start();
}
}
客户端:
package seday7;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
private Socket socket ;
/*
如果对象创建成功了。说明连接到服务器了 ,服务器端会给这个
客户端程序分配一个端口号。
localhost : 代表的是自己电脑 - 本机。 对应的ip: 127.0.0.1
*/
public Client() throws IOException {
System.out.println("开始连接服务器....");
socket = new Socket("localhost" , 9999);
System.out.println("服务器连接成功...");
}
// 启动客户端
public void start() throws IOException{
OutputStream outputStream = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(outputStream);
PrintWriter pw = new PrintWriter(osw);
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
pw.println(s);
pw.flush();
pw.close();
}
public static void main(String[] args) throws IOException {
Client c = new Client() ;
c.start();
}
}
1、Java容器都有哪些?
ArrayList、LinkedList、Vector、HashSet、LinkedHashSet、TreeSet、LinkedHashMap、TreeMap、ConcurrentHashMap、Hashtable
2.Collection 和 Collections 有什么区别?
Collection 是集合类的上级接口,继承它的主要有 List 和 Set;
Collections 是针对集合类的一个帮助类,它提供了一些列的静态方法实现,如 Collections.sort() 排序、Collections.reverse() 逆序等。
3.List、Set、Map 之间的区别是什么?
List:有序、可重复。
Set:无序、不可重复的集合。重复元素会覆盖掉。
Map:键值对,键唯一、值不唯一。Map 集合中存储的是键值对,键不能重复,值可以重复。
4、 HashMap 和 Hashtable 有什么区别?
HashMap是非线程安全的,HashTable是线程安全的。
HashMap的键和值都允许有null值存在,而HashTable则不行。
因为线程安全的问题,HashMap效率比HashTable的要高。
Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
5.如何决定使用 HashMap 还是 TreeMap?
对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。
6.说一下 HashMap 的实现原理?
HashMap采用位桶+链表实现,即使用链表处理冲突,同一hash值的键值对会被放在同一个位桶里,当桶中元素较多时,通过key值查找的效率较低。而JDK1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值(8),时,将链表转换为红黑树,这样大大减少了查找时间。
7.说一下 HashSet 的实现原理?
HashSet是基于HashMap实现的,HashSet中所有的元素都存放在HashMap的key上,而value中的值都是统一的一个固定的对象:private static final Object PRESENT = new
8.ArrayList 和 LinkedList 的区别是什么?
ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)双向链表的数据结构。
ArrayList其实最好需要指定初始容量的(当然有些程序员为了偷懒,根本不会去初始化,这其实对于代码质量低的项目而言是无关紧要的)《1.其实在代码规范里,都要手动设置,只是我们用默认而已,如果公司用一些代码规范的工具来做代码质量,会要求写上的 2.如果只是产品达到一定的量,初始容量还是有好处的》
LinkedList是比ArrayList灵活的,是根本不需要指定初始容量的
9.如何实现数组和 List 之间的转换?
数组转 List:使用 Arrays. asList(array) 进行转换。
List 转数组:使用 List 自带的 toArray() 方法。
10、 Array 和 ArrayList 有何区别?
数组(Array)是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按有序的形式组织起来的一种形式。这些有序排列的同类数据元素的集合称为数组。
数组是用于储存多个相同类型数据的集合
ArrayList就是动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了动态的增加和减少元素,实现了ICollection和IList接口,灵活的设置数组的大小等好处
11.哪些集合类是线程安全的?
Vector、HashTable、Stack、ArrayBlockingQueue、ConcurrentHashMap、ConcurrentLinkedQueue
12.迭代器 Iterator 是什么?
迭代器(iterator)有时又称光标(cursor)是程序设计的软件设计模式,可在容器对象(container,例如链表或数组)上遍访的接口,设计人员无需关心容器对象的内存分配的实现细节。
13、 Iterator 怎么使用?有什么特点?
• java.lang.Iterable 接口被 java.util.Collection 接口继承,java.util.Collection 接口的 iterator() 方法返回一个 Iterator 对象
• next() 方法获得集合中的下一个元素
• hasNext() 检查集合中是否还有元素
• remove() 方法将迭代器新返回的元素删除
• forEachRemaining(Consumer action) 方法,遍历所有元素
14.线程和进程的区别?
定义不一样,进程是执行中的一段程序,而一个进程中执行中的每个任务即为一个线程。
一个线程只可以属于一个进程,但一个进程能包含多个线程。
线程无地址空间,它包括在进程的地址空间里。
线程的开销或代价比进程的小。
17、创建线程有哪几种方式?有什么区别?
1.继承Thread类
2.实现Runnable接口
3.使用Callable接口
18、 说一下 runnable 和 callable 有什么区别?
1) Callable重写的是call()方法,Runnable重写的方法是run()方法
(2) call()方法执行后可以有返回值,run()方法没有返回值
(3) call()方法可以抛出异常,run()方法不可以
(4) 运行Callable任务可以拿到一个Future对象,表示异步计算的结果 。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果
19.在 Java 程序中怎么保证多线程的运行安全?
使用手动锁lock
使用线程安全类
使用自动锁synchronized关键字
使用volatile关键字
20.什么是死锁?
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去;此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
21.多线程产生死锁的 4 个必要条件?
1、互斥条件:任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请
者等待直到资源被占有者释放。
2、不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
3、请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
22、 怎么防止死锁?
1)由于资源互斥是资源使用的固有特性,无法改变,所以我们不讨论。
2)破坏不可剥夺条件:一个线程不能获得所需要的全部资源时便处于等待状态,等待期间它占有的资源将被隐式的释放,重新加入到系统的资源列表中,可以被其他的线程使用,而等待的线程只有重新获得自己原有的资源以及新申请的资源时才可以重新启动执行。
3)破坏请求与保持:
①第一种方式静态分配即每个线程在开始执行时就申请它所需的全部资源。
②第二种是动态分配即每个线程在申请所需要的资源时它本身不占用系统资源。
4)破坏循环等待条件:采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的、稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个线程只有获得了较小的编号才能申请较大的编号。
23.synchronized 和 Lock 有什么区别?
1.synchronized是关键字,Lock是接口;
2.synchronized是隐式的加锁,lock是显式的加锁;
3.synchronized可以作用于方法上,lock只能作用于方法块;
4.synchronized底层采用的是objectMonitor,lock采用的AQS;
24、 sleep() 和 wait() 有什么区别?
所在类不同
sleep()是Thread类的静态方法。
wait()是Object类的方法。
锁释放不同
sleep()是不释放锁的。
wait()是释放锁的。
用途不同
sleep()常用于一定时间内暂停线程执行。
wait()常用于线程间交互和通信。
用法不同
sleep()方法睡眠指定时间之后,线程会自动苏醒。
wait()方法被调用后,可以通过notify()或notifyAll()来唤醒wait的线程。
25、引用的四种对象?
1.强调引用
2.软引用
3.弱引用
4.虚引用
26、什么是 Java 序列化?什么情况下需要序列化?
序列化:将 Java 对象转换成字节流的过程。
反序列化:将字节流转换成 Java 对象的过程。
当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。
老师在讲内容的时候,我能够听懂,但是自己敲代码却敲不出来,还是对知识内容不够熟悉,在课余的时间还是需要自己多花一点时间在敲代码上,对于该如何调用,或者调用谁,没有弄清楚明白,整个四思维还是没有打开,在其余休假时间,通过自己的努力能够比以前有进步,加油!