《Java学习笔记1》
《Java学习笔记1》
*面向对象**
对象转换
假如我们定义一个父类Animal、一个子类Dog和一个子类cat
public class Animal{//父类}
class Dog extends Animal{//定义一个子类public void wang(){System.out.println("狗崽汪汪叫");}
}
class Cat extends Animal{//定义一个子类public void miao(){system.out.println("小猫崽喵喵叫");}
}
//调用
public class test{public static void mian(){Animal a= new Dog();//向上类型转换a.wang();//错误,编译器只认为a是Animal类型对象,需强转为Dog类型对象才能调用Dog d=(Dog) a;//强制类型转换,向下类型转换d.wang();//调用成功Cat c=(Cat) a;//编译通过,但是编译器认为a是Dog类型对象,不能强制转换c.miao();//调用不成功}
}
抽象类和抽象方法
.抽象方法
使用abstract修饰的方法,没有方法体,只有声明,定义的是一种‘规范’,就是告诉子类必须要给抽象方法具体的实现。
.抽象类
包含抽象方法的类就是抽象类,**抽象类只能用来继承**。通过Abstract方法定义规范,然后**要求子类必须定义具体的实现**,通过抽象类,我们可以做到严格限制子类的设计,以便子类之间更加通用。
abstract class Animal{//定义一个抽象类abstract public void shout();//定义一个抽象方法
}
class Dog extends Animal{//子类必须实现父类的抽象方法,否则编译错误public void shout(){//实现抽象方法system.out.println("汪汪汪");}public void seeDoor(){system.out.println("看门中---");}
}
接口interface
和抽象类的区别
比抽象类还抽象,抽象类里面还可以有一些具体实现了的方法,但是接口里面全是抽象方法对子类有了更加的规范的约束。
interface Volant{//飞行接口int fly_hight=100;//总是public static final 类型的void fly();//总是public abstract void fly()类型的
}
interface Honest{//善良接口void helpOther();
}
/*
*Angel类实现飞行和善良接口
*/
public class Angel implements Volant,Honest{public void fly(){system.out .println("我飞起来了");}public void helpOther(){system.out.println(”扶老奶奶过马路“);}
}
总结:
普通类:具体的实现
抽象类: 具体实现,规范(抽象方法)
接口:规范!
接口中定义静态方法和默认方法
默认方法
JAVA8之后,允许在接口中定义默认方法和类方法了。
默认方法和抽象方法的区别:抽象方法必须要实现,默认方法不是。这个接口的实现类都可以通过继承得到这个方法。
默认方法需使用到default关键字。
interface A{default void shout(){system.out.println("汪汪汪");}
}
class Test_A implements A{public void shout(){System.out.println("顶你个肺");}
}
静态方法
静态方法直接从属于接口(接口也是一种类,一种特殊的类),可以通过接口名调用。如果子类中定义了相同名字的静态方法,那就是两个完全不同的方法了,直接从属于子类。可以通过子类名调用。
接口的多继承
接口完全支持多继承,和类的继承类似,子接口拓展某个父接口,将会获得父接口中定义的一切。
String类
字符串相等判断---常量池原理
String str1=new String ("abcdefg");
String str2="abcdefg";
System.out.println(str1==str2);//返回false,这个是判断str是否和str2相等
//涉及到字符串比较的时候都用equals()方法
System.out.println(str1.equals(str2));//返回的是true,这个是判断str1里面的值是否和str2里面的值相等
-
String类的常用方法列表
方法 解释说明 char charAt(int index) 返回字符串中第index个字符 boolean equals(String other) 两个字符串的值相等返回true;否则返回false boolean equalsIgnoreCase(String other) 两个字符串的值相等(忽略大小写)返回true;否则返回false int indexOf(String str) 返回从头开始查找第一个字符串str在字符串中的位置,未找到返回-1 lastIndexOf(String str) 返回从结尾开始查找第一个字符串str在字符串中的位置,未找到返回-1 int length() 返回字符串的长度 String replace(char oldChar, char newChar) 返回一个新字符串,它是新串替换字符串里面的所有oldChar生成的 boolean startsWith(String prefix) 如果字符串以prefix开头,则返回true boolean endsWith(String prefix) 如果字符串以prefixji结尾,则返回true String substring(int beginIndex) 返回一个字符串,该串包含从原始字符串的beginIndex位置开始到串尾 String substring(int beginIndex,int endIndex) 返回一个从beginIndex位置到(endIndex-1)位置的所有字符 String toLowerCase() 返回一个新字符串,原始字符串大写变小写 String toUpperCase() 返回一个新字符串,原始字符串字符小写变大写 String trim() 返回一个新字符串,该串删除了原始字符串头部和尾部的空格
内部类
内部类是一类特殊的类, 指的是一个定义在一个类的内部的类,在实际开发中,为了方便使用外部类的相关属性和方法,这时候我们通常会定义一个内部类。
-
内部类的作用
内部类提供了更好的封装,只能让外部类使用,直接访问,不允许同一个包中的其他类直接访问,
内部类可以直接访问外部类的私有属性,内部类被当成外部类的成员,但外部类不能访问内部类的内部属性。
public class Outer{//外部类private int age=10;private void show(){System.out.println("要你好看");}//内部类public class Inner{private String name="谢洪凯";private int age=20;private void shows(){System.out.println("Inner.shows");System.out.println(age);System.out.println(Outer.this.age);//当外部类和内部类发生重名时可以通过Outer.this.成员名来调用show();//内部类可以直接使用外部类的成员}}public static void main(){outer.Inner A=new outer().new Inner();//定义内部类的对象的方式A.shows();//调用内部类方法//另一种写法Outer.outer=new Outer();Inner.inner=outer.new Inner();inner.shows();}
}
内部类分类:
-
1.成员内部类
-
非静态内部类
-
静态内部类
-
-
2.匿名内部类
-
3.局部内部类
非静态内部类:
非静态内部类(外部类使用非静态内部类和平时使用其他类没有什么不同)
-
非静态内部类必须寄存在一个外部类对象里面,因此如果存在一个非静态内部类对象,那么一定存在一个对应的外部类对象,非静态内部类对象单独属于外部类的某个对象;
-
非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员;
-
非静态内部类不能有静态方法,静态属性和静态初始化块。
静态内部类:
定义方式:
static class ClassName{//类体
}
使用要点:
-
1、静态内部类可以访问外部类的静态成员,不能访问外部内的普通成员;
-
2、静态内部类看作外部类的一个静态成员。
匿名内部类:
适合那种只需要使用一次的类,比如键盘监听操作等等,在安卓开发、awt、swing、开发中常见。
语法:
new 父类构造器(实参类表)\实现接口 (){
//匿名内部类类体!
}
eg:
public class TestInnerClass{public void Test(A a){a.run();}public static void main(){TestInnerClass T=new TestInnerClass();T.Test(new AA());T.Test(new A(){//新建一个匿名类,用完之后就没了@Overridepublic void run(){System.out.println("你好S啊");}});T.Test(new A(){//再新建一个匿名类@Overridepublic void run(){System.out.println("对不起!你不S");}});}
}
class AA implements A{public void run(){System.out.println("你是真的S");}
}
public interface A{void run();
}
局部内部类:
定义在方法内部的,作用域只限于本方法,称为局部内部类。
数组
数组的定义:
数组是相同类型数据的有序集合。
-
长度是确定的,数组一旦被创建,它的大小是不可以被改变的;
-
其元素的类型必须是相同的类型,不允许出现混合型;
-
数组类型可以是任何数据类型,包括基本类型和引用类型;
-
数组变量属于引用类型,数组也是对象。
老鸟的建议:数组中的每个元素相当于该对象的成员变量。数组本身就是对象,JAVA中对象是堆中的,因此·数组无论是保存原始类型还是其他对象类型,数组对象本身是在堆中存储的。
public class Test { public static void main(String[] args) {int[] S=null;//声明一个数组S=new int[10];//给数组分配空间for(int i=0;i<10;i++){S[i]=2*i+1;//给数组元素赋值,数组也是对象,数组中的元素就是对象的属性System.out.println(S[i]);}
}
数组的默认初始化
int array[]=new int[2];//默认值:0 ,0
boolean[] b=new boolean[2];//默认值:false,false
String[] str=new String[2];//默认值:null,null
数组的动态初始化
int array[]=new int[2];
array[0]=1;
array[1]=2;
数组的静态初始化
int array[]={1,2,3};//静态初始化基本类型数组;
Man[] mans={new Man(1,2),new Man(1,4)};//静态初始化引用类型数组
数组的遍历方法
-
for循环
int[] a={1,2,3,4,5,6,7,8,9} for( int i=0;i<a.length;i++){ System.out.println(a[i]); }
-
for-each循环
String[] ss={"aaa","bbb","ccc","ddd"}; for(String temp:ss){ System.out.println(temp); }
注意事项:
-
for-each增强for循环在遍历数组过程中不能修改数组中某元素的值;
-
for-each仅适用于数组的遍历,不涉及有关索引、下标的操作。
-
数组的拷贝
System类里也包含了一个static void arraycopy(object src,int srcpos,object dest,int destpos,int length)方法,该方法可以将src数组里的元素值赋给dest数组的元素其中srcpos指定从src数组的第几个元素开始赋值,length指定将src数组的多少个元素赋给dest数组的元素。
public class Test {public static void main(String[] args) {String[] strings={"阿里","尚学堂","京东","搜狐","网易"};String[] sBak=new String[6];System.arraycopy(strings,0, sBak,0, strings.length);for(int i=0;i<sBak.length;i++){System.out.print(sBak[i]+'\t');}}
}
Java.util.Arrays类
该类包含了常用的数组操作,方便我们的日常开发,Arrays类包含了排序,查找,填充,打印内容 等常见的操作。
使用Arrays对数组元素打印
import java.util.Arrays;
public class Test {public static void main(String[] args) {int[] a={1,2};System.out.println(a);//打印数组引用的值System.out.println(Arrays.toString(a));//打印数组元素的值}
}
使用Arrays对数组元素进行排序
import java.util.Arrays;
public class Test {public static void main(String[] args) {int[] a={1,3,2,4,5,9,7,8,6};System.out.println(Arrays.toString(a));Arrays.sort(a);System.out.println(Arrays.toString(a));}
}
使用Arrays实现二分法查找
import java.util.Arrays;
public class Test {public static void main(String[] args) {int[] a={1,3,2,4,5,9,7,8,6};System.out.println(Arrays.toString(a));Arrays.sort(a);//使用二分法查找,必须先排序System.out.println(Arrays.toString(a));//返回排序后新的索引位置,若未找到返回负数System.out.println("该元素的索引:"+Arrays.binarySearch(a, 5));}
}
使用Arrays类对数组进行填充
import java.util.Arrays;
public class Test {public static void main(String[] args) {int a[]={11,12,23,43,545,656};System.out.println(Arrays.toString(a));Arrays.fill(a, 2,4,100);//填充,将2到4索引的元素替换为100System.out.println(Arrays.toString(a));}
}
多维数组
什么是多维数组:
简单点来说就是数组里面的元素还是数组。
二维数组
public class Test {public static void main(String[] args) {int[][] a={{1,2},{1,12,3},{1,23,4,5}};System.out.println(a[2][3]);}
}
如何获取二维数组的长度
//获取二维数组中第一维数组的长度
System.out.println(a.length);
//获取二维数组中第二维数组中第一个数组的长度
System.put.println(a[0].length);
Object数组存取表格数据
import java.util.Arrays;
public class Test {public static void main(String[] args) {Object[] a1={"001","x1","12010"};//定义一维数组,里面填充表里面的数据Object[] a2={"002","x2","10211"};//定义一维数组,里面填充表里面的数据Object[] a3={"003","x3","12011"};//定义一维数组,里面填充表里面的数据Object[][] emps=new Object[3][];//定义一个Obiect二维数组emps[0]=a1;//把上面填充好了的一维数组数据赋给该二维数组的第二维数组里面emps[1]=a2;emps[2]=a3;System.out.println(Arrays.toString(emps[0]));System.out.println(Arrays.toString(emps[1]));System.out.println(Arrays.toString(emps[2]));}
}
使用javabean和一维数组保存表格信息
import java.util.Arrays;
/**使用javabean和一维数组来存取表格信息*/
public class Test {public static void main(String[] args) {Emp emp0=new Emp(0101,"高小一",18,"程序员","2019-10-1");Emp emp1=new Emp(0102,"高小二",19,"程序员","2019-10-2");Emp emp2=new Emp(0103,"高小三",20,"程序员","2019-10-3");Emp[] emps=new Emp[3];emps[0]=emp0;emps[1]=emp1;emps[2]=emp2;for(int i=0;i<emps.length;i++){System.out.println(emps[i].toString());}}
}class Emp{private int id;private String name;private int age;private String job;private String hiredate;public Emp(){};public Emp(int id,String name,int age,String job,String hiredate){this.id=id;this.name=name;this.age=age;this.job=job;this.hiredate=hiredate;}public int getId() {return id;}public void setId(int id) {this.id = id;}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;}public String getJob() {return job;}public void setJob(String job) {this.job = job;}public String getHiredate() {return hiredate;}public void setHiredate(String hiredate) {this.hiredate = hiredate;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn "["+id+"\t"+name+"\t"+age+"\t"+job+"\t"+hiredate+"]";}}
Comparable接口
该接口中只有一个方法comparaTo(),这个方法定义了对象之间的比较规则,依据这个比较规则,我们就能实现对象的排序。
事实上,java中排序算法也依赖Comparable接口。
Comparable接口中只有一个算法:(大于返回1,等于返回0,小于返回-1)
public int comparaTo(Object obj)//obj为要比较的对象
常见算法
冒泡排序算法
如果大家想详细的理解算法的话,去这个网站:VisuAlgo - visualising data structures and algorithms through animation
int temp,i;
for(i=0;i<values.length;i++){boolean flag=true;for(int j=0;i<values.length-1-i;j++){if(values[j]>values[j+1]){temp=values[j];values[j]=values[j+1];values[j+1]=temp;flag=false;}}
}
二分法查找
二分法检索(binary search),又称折半检索。二分法的基本思想是设数组中的元素从小到大排列存放在数组Array中,首先将给定值key与数组中间位置上的元素的关键码key比较,如果相等,则检索成功。
否则,若key小,则在前半部分中继续二分检索。
若key大,则在数组后半部分中继续二分检索。这样,经过一次检索就缩小一半的检索空间,如此进行下去,直到检索成功或检索失败。
//二分法基本代码
public static void BinarySearch(int array[],int value){//要查找的数组和要查找的值 int low=0;int high=array.length-1;while(low<=high){middle=(high-low)/2;if(value==array[middle]){return middle;//找到了返回查找数的索引}if(value<array[middle]){high=middle-1;}if(value>array[middle]){low=middle+1;}}return -1;//未找到返回-1
}
常用类
1.基本数据类型的包装类
(1)final class:子类不能被继承或者说没有子类。
(2)所有“数字型”包装类与Number之间的继承关系,Number类是一个抽象类。
(3)Number是一个抽象类,因此它的所有抽象方法都需要提供实现。Number类提供了抽象方法intValue()、longValue()、floatValue()、doubleValue()四种方法,意味着所有的“数字型”包装类都可以相互转型。
public class Test {public void TestInteger(){//基本数据类型转为对象Integer integer=new Integer(300);Integer integer2=integer.valueOf(200);System.out.println(integer+integer2);//包装类类对象转为基本数据类型double d=integer2.doubleValue();System.out.println(d);//字符串转为Integer对象Integer integer3=integer.parseInt("4000");System.out.println(integer3);Integer integer4=new Integer("500");//Integer对象转为字符串String str=integer3.toString();//一些常见int类型相关的常量System.out.println("int能表示的最大整数:"+integer.MAX_VALUE);}public static void main(String[] args) {Test test=new Test();test.TestInteger();}
}
(4)包装类的基础知识
Java是面向对象的语言,但不是纯面向对象,以为我们经常用到的基本数据类型就不是对象,但是在实际运用中,我们经常需要将基本数据转化为对象,以便于操作,比如:将基本数据类型存储到Object[]数据或集合中的操作等等。
基本数据类型 | 包装类 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
(5)在以后的编程中,我们会频繁的用到基本数据类型和包装类对象之间的转换。
比如当我们想把一个数字转为一个字符串的话,下面这段代码中的i不是一个对象,因而会报错,需把i转为一个对象之后才能被转为字符串。
//转为对象之前
public class Test {public void TestInteger(){int i=300;String string=i.toString();//报错,因为i不是一个对象 System.out.println(string);
}public static void main(String[] args) { Test test=new Test();test.TestInteger();}
}
//转为对象之后
public class Test {public void TestInteger(){int i=300;Integer integer=Integer.valueOf(i);//将i转为Integer类对象String string=integer.toString();System.out.println(string);}public static void main(String[] args) {Test test=new Test();test.TestInteger();}
}
(6)总结,我们的包装类可以干甚:
包装类可以把基本数据类型,包装类对象还有我们的字符串,这三个之间来回转化,包装类对象起核心的作用,把数据和字符串来回转。比如想把一个字符串转为一个数据,需先把字符串喜欢为对象,才能转为数据。
2.自动装箱和自动拆箱
自动装箱
基本类型的数据处于需要对象的环境时,会自动转为对象。
以Integer为例:
在JDK5之前,Integer i=5这样的代码是错误的,必须通过Integer i=new Integer(5)这样的语句来实现基本数据类型转为包装类的过程。在之后,JAVA提供了自动装箱的功能,因此只Integer i=5这样的语句就能事项其间的转化。这是因为JVM为我们执行了Integer i=Integer.valueOf(5)这样的操作,这就是java的自动装箱。
自动拆箱
每当需要一个值时,对象会自动转为基本数据类型的包装类,没必要再去调用转型方法,如:Integer i=5;int j = i;这样的过程就是自动拆箱。
包装类空指针异常报错
public class Test {public void TestInteger(){Integer i=null;//i是空,不能调用其方法int j=i;//编译器帮你改成i.intvalue();但是i为空,不能调用方法}public static void main(String[] args) {Test test=new Test();test.TestInteger();}
}
3.包装类的缓存问题
public class Test {public void TestInteger(){Integer a=3000; Integer b=3000;//如果数字在[-128,127]之间的时候,返回的是缓存数组中的某个元素Integer c=123;Integer d=123;System.out.println(a==b);//两个不同的对象,返回falseSystem.out.println(c==d);//因为c,d对象返回的都是同一个数组(缓存数组)中的元素,所以返回trueSystem.out.println(a.equals(b));//返回true}public static void main(String[] args) {Test test=new Test();test.TestInteger(); }
}
4.自定义一个包装类
package MyInteger;
import java.lang.management.MemoryUsage;
public class MyInteger {public static final int low=-128;public static final int high=127;private int value;private static MyInteger[] cache=new MyInteger[256];static{//静态初始化//[-128,127]缓存进来for(int i=low;i<high;i++){//-128->0,-127->1,-126->2cache[i+128]=new MyInteger(i);}}private static MyInteger valueOf(int i) {if(i>=low&&i<=high){return cache[i+128];}return new MyInteger(i);}private MyInteger(int i) {this.value=i;}@Override public String toString() {// TODO Auto-generated method stubreturn this.value+"";}public static void main(String[] args) {MyInteger myInteger=MyInteger.valueOf(300);System.out.println(myInteger);}
}
5.字符串相关类
String类、StringBuilder类、StringBuffer类是三个字符串相关类。String类是对象代表不可变的字符序列,StringBuilder类和StringBuffer类代表可变字符序列。关于这三种类详细的用法,笔试的时候会用到,必须掌握好。
StringBuilder类和StringBuffer类的区别(二者均是抽象类AbstractStringBuilder的子类):
-
StringBuffer 线程安全,做线程同步检查,效率很低。
-
StringBuilder 线程不安全,不做线程同步检查,因此效率高,建议采用该类。
常用方法列表:
方法 | 属性 |
---|---|
重载的public StringBuilder append(...)方法 | 可以为StringBuilder 对象添加字符序列,仍然返回自身对象 |
public StringBuilder delete(int start,int end) | 删除从start到end-1的一段字符序列,仍然返回自身对象 |
public StringBuilder deleteCharAt(int index) | 移除指定位置上的char,仍然返回自身对象 |
重载的public StringBuider insert(...) | 为StringBuilder对象在指定位置插入字符序列,仍然返回自身对象 |
public StringBuilder reverse() | 用于将字符序列逆序,仍然返回自身对象 |
public String toString() | 返回此字符序列中数据的字符串表示形式 |
和String类含义类似的方法
方法 | 属性 |
---|---|
public indexOf(String str) | 返回该字符串的位置,没有返回-1 |
public int indexOf(String str,int fromIndex) | 返回从fromIndex开始查找字符串str的位置,没有返回-1 |
public String subString(int start) | 返回从start位置开始到最后一个字符的字符串 |
public String subString(int start,int end) | 返回一个从start位置到(end-1)位置的所有字符 |
public int length() | 返回字符串的长度 |
char charAt(int index) | 返回字符串中第index个字符 |
注意它的上面几种方法返回的都是自身对象(return this)
例如append()方法
package MyInteger;
import java.lang.management.MemoryUsage;
public class MyInteger {public static void main(String[] args) {String str1="abcd";//不可变字符序列StringBuffer sb2=null;//可变字符序列,线程安全,效率低StringBuilder sb3=null;//可变字符序列,线程不安全,效率高sb2=new StringBuffer("gao");sb2.append(123);sb2.append(456);System.out.println(sb2);//还可以这样写sb2.append("aa").append("bb").append("cc");System.out.println(sb2);}
}
不可变和可变字符序列使用陷阱
-
String使用陷阱
String已经初始化就不能再改变其内容了。对String字符的操作实际上是对其副本的操作,原来的字符串一点都没有改变,比如:创建一个字符串String str="abc";str=str+"defg";实际上原来的字符串abc已经丢弃了现在又产生一个新的字符串,如果多次执行改变字符串内容的操作,会导致大量副本字符串留在内存中,效率降低如果这种操作放在循环当中,会严重影响时间和空间的性能。甚至会导致服务器的崩溃。
package MyInteger
import java.lang.management.MemoryUsage;
public class MyInteger {public static void main(String[] args) {//使用String类System.out.println("string类");String str=null;long time=Runtime.getRuntime().freeMemory();//获取当前系统剩余内存空间System.out.println(time);long time2=System.currentTimeMillis();//获取系统的当前时间System.out.println(time2);for(int i=0;i<10000;i++){str=str+i;}long time3=Runtime.getRuntime().freeMemory();System.out.println(time3);long time4=System.currentTimeMillis();System.out.println(time4);System.out.println("运行之后占用了"+(time3-time)+"的内存空间");System.out.println("运行了"+(time4-time2)+"秒");System.out.println();//使用StringBuffer类System.out.println("stringBuffer类");StringBuffer str1=new StringBuffer("");long num=Runtime.getRuntime().freeMemory();//获取当前系统剩余内存空间System.out.println(num);long t=System.currentTimeMillis();//获取系统的当前时间System.out.println(t);for(int i=0;i<10000;i++){str1.append(i);}long num2=Runtime.getRuntime().freeMemory();System.out.println(num2);long t2=System.currentTimeMillis();System.out.println(t2); System.out.println("运行之后占用了"+(num-num2)+"的内存空间");System.out.println("运行了"+(t2-t)+"秒");}
}
开发中容易造成内存泄漏的操作
-
创建大量无用对象
String str=null; for(int i=0;i<10000;i++){str=str+i;//相当于产生了10000个String对象 }
-
静态集合类的使用
像HashMap、Vector、List等的使用最容易出现内存泄露,这些静态的生命周期和应用程序一致,所有的对象Object也不能释放。
-
各种连接对象(IO流对象,数据库连接对象,网络连接对象)未关闭,其属物理连接,需关闭。
-
监听器的使用
释放对象时,没有删除相应的监听器
6.时间处理相关类
“时间如流水,一去不复返”,在计算机世界,我们把1970年1月1日00:00:00定位基准时间,每个度量单位是毫秒(1秒的千分之一)。
如果想获得现在是可的时刻数值,可以使用Long time=System.currentTimeMillis();
时间日期相关类:
Date时间类(java.util.Date)
package MyInteger;
import java.util.Date;
import javax.xml.crypto.Data;
public class MyInteger {public static void main(String[] args) {Date d1=new Date(1000L*3600*24*365*250);//传参代表1970年1月1号加上括号里面的时间System.out.println(d1);Date d2=new Date();//不传参则代表当前时间System.out.println(d2);System.out.println(d2.getTime());}
}
方法 | 属性 |
---|---|
Date() | 分配一个Date对象,并初始化此对象为系统当前的日期和时间,可以精确到毫秒 |
Date(long date) | 分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元”,即1970年1月1日00:00:00 GMT)以来指定毫秒数 |
boolean after(Date when) | 测定此日期是否在指定日期之后 |
boolean before(Date when) | 测定此日期是否在指定日期之前 |
boolean equals(Object obj) | 比较两个日期的相等性 |
long getTime() | 返回1970年以来date对象表示的毫秒数 |
String toString() | 把此Date对象转换成以下形式的String: |
dow mon dd hh:mm:ss:zzz yyyy 其中dow表示一周中的某一天 |
DateFormat类SimpleDateFormat类
-
DateFormat类的作用
把时间对象转化为指定格式的字符串,反之,把指定的字符串转化为时间对象。
它是一个抽象类,一般使用他的子类SimpleDateFormat来实现。
package MyInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;/* * 测试时间对象和和字符串的相互转化 * 使用DateFormat * 和SimpleDateFormat */
import java.util.Date;
public class MyInteger {public static void main(String[] args) throws ParseException {SimpleDateFormat df=new SimpleDateFormat("YYYY-MM-dd hh:mm:ss");//将字符串转化为Date类对象Date d1= df.parse("1970-1-1 10:44:22");System.out.println(d1.getTime());//将Date对象转为字符串Date d2=new Date();String str=df.format(d2); System.out.println(str);}
}
CheckedException 已检查异常
所有不是RuntimeException的异常,都统称为已检查异常。如IOException、SQLException等以及用户自定义的Exception异常这类异常在编译时是就必须做出处理,否则无法通过编译。
异常的处理方式有"try/catch"捕获异常、使用"throws"声明异常。
日历Calendar类
-
Calendar类是一个抽象类,为我们提供了日期计算的相关功能,比如年、月、日、时、分、秒的展示和计算。
-
GregorianCalendar是Calendar的一个具体子类,提供了世界上大多数国家的/地区的使用的标准日历系统。
-
菜鸟雷区:
注意月份的表示,一月是0,二月是1,以此类推,12月是11.因为大多数人习惯使用单词来表示月份而不是使用数字,这样程序也容易读,父类使用常量来表示月份,January、February等等。
Calendar 类常用方法的记录
获取时间
// 使用默认时区和语言环境获得一个日历Calendar cal = Calendar.getInstance();// 赋值时年月日时分秒常用的6个值,注意月份下标从0开始,所以取月份要+1System.out.println("年:" + cal.get(Calendar.YEAR));System.out.println("月:" + (cal.get(Calendar.MONTH) + 1)); System.out.println("日:" + cal.get(Calendar.DAY_OF_MONTH));System.out.println("时:" + cal.get(Calendar.HOUR_OF_DAY));System.out.println("分:" + cal.get(Calendar.MINUTE));System.out.println("秒:" + cal.get(Calendar.SECOND));
//运行结果
年:2018
月:2
日:12
时:15
分:57
秒:39
设置时间
月份的下标从 0 开始,设置时同样需要注意,比如我们设置为 2 月 15 日除夕当晚的倒计时的最后一秒: 2018-02-15 23:59:59 可以这样:
Calendar cal = Calendar.getInstance();// 如果想设置为某个日期,可以一次设置年月日时分秒,由于月份下标从0开始赋值月份要-1// cal.set(year, month, date, hourOfDay, minute, second);cal.set(2018, 1, 15, 23, 59, 59);
或者也可以单个字段一一设置:
// 或者6个字段分别进行设置,由于月份下标从0开始赋值月份要-1cal.set(Calendar.YEAR, 2018);cal.set(Calendar.MONTH, Calendar.FEBRUARY);cal.set(Calendar.DAY_OF_MONTH, 15);cal.set(Calendar.HOUR_OF_DAY, 23);cal.set(Calendar.MINUTE, 59);cal.set(Calendar.SECOND, 59);System.out.println(cal.getTime());
//打印的时间结果为Thu Feb 15 23:59:59 CST 2018
时间计算
add方法: 比如在除夕当晚最后一秒,add 一秒:
Calendar cal = Calendar.getInstance();System.out.println(cal.getTime());cal.set(2018, 1, 15, 23, 59, 59);cal.add(Calendar.SECOND, 1);System.out.println(cal.getTime());
//打印时间结果如下,日期会自动进入下一天:Thu Feb 15 23:59:59 CST 2018Fri Feb 16 00:00:00 CST 2018
再比如 1 月 31 号的时候,月份加一,会出现怎样结果:
Calendar cal = Calendar.getInstance();cal.set(2018, 1, 31, 8, 0, 0);System.out.println(cal.getTime());cal.add(Calendar.MONTH, 1);System.out.println(cal.getTime());
//结果为Wed Jan 31 08:00:00 CST 2018Wed Feb 28 08:00:00 CST 2018
//说明 add 月份时,会将不存在的日期归为当月日历的最后一天。
7.其他常用类:
Math类
-
Math类的常用方法:
方法 属性 abs 绝对值 acos,asi,atan,cos,sin,tan 三角函数 sqrt 平方根 pow(double a,double b) a的b次幂 max(double a,double b) 取大值 min(double a,double b) 取小值 ceil(double a) 大于a的最小整数 floor(double a) 小于a的最小整数 random() 返回0-1的随机数 long round(double a) double型的数据a转换为long型(四舍五入) toDegrees(double angrad) 弧度转角度 toRadians(double angdeg) 角度转弧度
Random类
这个类是专门用来生成随机数的,并且Math.random()底层调用的就是Random类的nextDouble()方法。
File类用来代表文件和目录。
1.File类的基本用法
java.io.File类,代表文件和目录。在开发中,读取文件,生成文件,删除文件,修改文件的属性是经常会用到本类。
-
File类的常见构造方法Public File(String pathname).
以pathname为路径,创建File类对象,如果pathname是相对路径,则默认的当前路径是在系统user.dir中储存。
package MyInteger;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
public class MyInteger {@SuppressWarnings("unused")public static void main(String[] args) throws ParseException, IOException {File f1=new File("");File f2=new File("也可以放一个目录");System.out.println(System.getProperty("user.dir"));//获得项目所在的路径f1.createNewFile();//根据f1的路径创建一个文件 }
}
2.File类访问属性的方法列表
方法 | 说明 |
---|---|
publoic boolean exists() | 判断File是否存在 |
public boolean isDirectory() | 判断File是否是目录 |
public Boolean isFile() | 判断是否是文件 |
public long lastModifiled() | 返回File最后修改时间 |
public long length() | 返回File的大小 |
public String getName() | 返回文件名 |
public String getPath() | 返回文件目录路径 |
package Myiava;
import java.io.File;
import java.util.Date;
public class TestFile {public static void main(String[] args) {File f1=new File("d:/b.txt");System.out.println("该文件是否存在:"+f1.exists());System.out.println("该文件是否是目录文件:"+f1.isDirectory());System.out.println("File是否是文件:"+f1.isFile());System.out.println("File的大小:"+f1.length());System.out.println("文件名:"+f1.getName());System.out.println("文件的目录路径:"+f1.getPath());System.out.println("文件的最后修改时间:"+new Date(f1.lastModified()));}
}
3.通过File对象创建文件或目录(在该对象的文件或目录不存在的情况下)
-
File创建文件或目录的方法列表
方法 说明 creatNewFile() 创建新的File delete() 删除File对应的文件 mkdir() 创建一个目录,中间一个目录缺失,则目录创建失败 mkdirs() 创建多个目录,中间某个目录缺失,则创建该缺失目录
枚举
枚举类型的定义包括枚举声明和枚举体。格式如下:
enum 枚举名{ 枚举体(常量列表)}//创建一个枚举类型
package Myiava;
public class MySeason {enum season{ SPRING,SUMMER,AUTUMN,WINDER }enum week{星期一,星期二,星期三,星期四,星期五,星期六,星期日 }public static void main(String[] args) {System.out.println(week.星期三);System.out.println(season.AUTUMN);}
}
老鸟建议(枚举实际上也是一个类)
-
当你定义一组常量时,可以使用枚举类型,
-
尽量不要使用枚举的高级特性,事实上高级特性可以使用普通类来实现,没有必要使用枚举,增加程序的复杂性。
不想用枚举,这样也可以
package Myiava;
public class MySeason { public static final int SPRING=0;//不想写枚举,这样也可以public static final int SUMMER=1;public static final int AUTUMN=2;public static final int WINDER=3;public static void main(String[] args) {System.out.println(MySeason.SUMMER);System.out.println(MySeason.WINDER); }
}
枚举的遍历:
package Myiava;
public class MySeason {enum season{SPRING,SUMMER,AUTUMN,WINDER }enum week{星期一,星期二,星期三,星期四,星期五,星期六,星期日 }public static void main(String[] args) {System.out.println(week.星期三);System.out.println(season.AUTUMN); //枚举的遍历 for(week k:week.values()){System.out.println(k); } }
}
package Myjava;
import java.util.Random;
public class MySeason {enum season{SPRING,SUMMER,AUTUMN,WINDER }enum week{星期一,星期二,星期三,星期四,星期五,星期六,星期日 }public static void main(String[] args) {System.out.println(week.星期三); System.out.println(season.AUTUMN); Random random=new Random(); //枚举的遍历for(week k:week.values()){ System.out.println(k); } int a=random.nextInt(8);//获取0~8的随机数 switch (week.values()[a]) { case 星期一: break; case 星期二: break; case 星期三: break; default:System.out.println("你真的S"); break; }}
}
递归结构
递归的基本思想就是"自己调用自己",一个使用递归技术的方法将直接或间接地调用自己。
一般递归地实现
package Myjava;
public class Test { public static int count=0; public static void a() {count++; System.out.println("Test.a"); if(count>0&&count<10){ a();//递归调用 }}public static void b(){ System.out.println("Test.b");} public static void main(String[] args) {a();}
}
阶乘的递归实现
package Myjava;
public class Test {public static long f(int n){if(n==1){ return 1; } else{ return n*f(n-1);} } public static void main(String[] args) {System.out.println(f(20));}
}
递归遍历目录结构和树状展现
package Myjava;
import java.io.File;
public class Field {static void printFile(File file ,int level){for (int i = 0; i <level; i++) {System.out.println("-");} System.out.println(file.getName());//获取文件名并输出文件名if(file.isDirectory()){//判断此文件是否是目录文件File[] files=file.listFiles(); for(File temp:files){ //递归调用该方法 printFile(temp,level); } } }
}
异常
主要内容
-
1.异常机制
-
异常的处理
-
百度:超级搜索
假如没有异常机制,那么我们再写某个模块的时候,可能会遇到很多情况,那么我们就需要使用if语句来处理它了。
例如;我们要拷贝一个文件。
public class Test1{public static void main(String[] args){//将d:/a.text复制到e:/a.textif("d:/a.text"这个文件存在){if(e盘的空间大于a.text的文件长度){if(文件复制到一半IO流断掉){System.out.println(" 停止copy,输出IO流出现问题");}else{copyFile("d:/a.text","e:/a.text");}}else{System.out.println("e盘空间不够存放a.text");}}else{System.out.println("该文件不存在!!!"); }}
}
这种方式有两个坏处:
1.逻辑代码和错误代码放在一起。
2.程序员本身要考虑的例外情况较复杂,对程序员要求较高。
针对如上,Java异常机制给我们提供了方便的处理方式
异常机制本质
当程序出现错误,程序是安全的,继续执行的机制。
2.异常的概念
异常是指程序运行过程中出现的非正常现象,例如用户输入错误、除数为零、需要处理的文件不存在、数组下标越界等等。
在Java的异常处理机制中引进了很多用来描述和处理异常的类,成为异常类。异常类定义中包含了该类的异常的信息和对异常进行处理的方法。
所谓异常处理就是出现问题时依旧可以正确的执行完。
Java是采用面向对象的方式来处理异常的,处理过程:
-
抛出异常
在执行一个方法时,如果发生异常,则该方法生成一个代表该异常的一个对象,停止当前执行路径,并把异常对象提交给JRE
-
捕获异常
JRE得到该异常后,寻找相应的代码来处理该异常,JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常代码为止。
3.异常的分类
所有的异常的根类是java.Lang.Trowable,Trowable又派生出了两个子类Error和Exception。
Java异常类的层次结构图如下所示;
Error
Error是程序无法处理的异常,表示运行应用程序中较为严重的问题,大多数错误与代码编写者执行的操作无关,而表示代码运行时JVM虚拟机出现了问题,例如Java虚拟机运行错误,当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError,这些异常发生时,JVM一般会选择栈程终止。
Exception
Exception是程序本身能够处理的异常,如空指针异常、数组下标越界异常、类型转换异常、算数异常等等。
Exception是所有异常类的父类,其子类对应了各种各样的可能出现的异常事件,通常Java的异常可分为:
-
RuntimeException 运行时异常
-
CheckedException 已检查异常
RuntimeException 运行时异常
派生于RuntimeException的异常,如被0除、数组下标越界、空指针等,其产生比较频繁,处理比较麻烦。如果显示的声明或捕获将对程序可读性和运行效率影响很大,因此由系统自动检测并交给缺省的异常处理程序(用户可不必对其处理)
这类一场通常是由编程错误导致的,所以在编程时,并不要求必须使用异常处理机制来处理这类异常,经常要通过添加"逻辑处理来避免这些异常"。
//对被0除异常的逻辑处理
public class Test1{public static void main(String[] args){int a=0;if(a!=0){System.out.println("1/a");}}
}
解决ClassCastException 类型转换异常的典型方式
public class Test{public static void main(String[] args){Animal a= new Dog();if(a instanceof Cat){Cat c = (Cat) a;}}
}
数字格式化异常的解决方法,可以引入正则表达式判断是否是数字
public class Test{public static void main(String[] args){String str="12345bhdk";Pattern p=Pattern.compile("^\\d+$");Matcher m=p.matcher(str);if(m.matches()){//如果str匹配代表数字的正则表达式,才会转换System.out.println(Integer.parseInt(str));}}
}
CheckedException 已检查异常
所有不是RuntimeException的异常统称为已检查异常,如IOException、SQLException等以及用户自定义的一些Exception异常。这些异常在编译时就必须做出处理,否则无法通过编译。
4.异常的处理
异常的处理方式之一:捕获异常
捕获异常是通过3个关键词来实现的,try-catch-finally。用try来执行一段程序,如果出现异常,则系统抛出一个异常,可以通过他的类型来捕捉(catch)并处理它,最后是通过finally语句来为异常处理提供一个统一的出口。finally所指定的代码都要被执行,(catch)语句可以有多条,finally语句最多只能有一条,根据自己的需要,可有可无。
注意:在定义多个Catch语句时,如果遇到子类和父类时,子类放前面,父类放后面,没有父子关系无所谓
常用开发环境中,自动增加try-catch代码块的快捷键
-
将需要处理异常的代码选中
-
IDEA中,使用:ctrl+Alt+t
-
在eclipse中,使用ctrl+shift+z
异常的处理方式之二:声明异常(throw字句)
当CheckedException产生时,不一定立刻要处理它,可以把异常Throws抛出去。
在方法中使用try-catch-finally是由这个方法来处理异常。但在一些情况下,并不需要处理发生的异常,而是向上传递给调用它的方法处理。
如果一个方法中可能产生某种异常,但是并不确定如何处理这种异常,则应根据异常规范在方法的首部声明该方法可能出现的异常。
如果一个方法抛出多个已检查异常,则必须在方法的首部声明所有的异常,之间以逗号隔开。
注意——》例如:main方法调用readFile方法,就由main方法来处理异常,readFile不用管,谁调用我谁来处理,main方法也可以抛出,谁调用它谁来处理异常。
public static void readFile(String fileName)throws FileNotFoundException,IOexception{FileReader in = new FileReader(fileName);...
}
try-with-resource 自动关闭Closable接口的的资源
JDK7之后,新增了"try-with-resouce"它可以自动关闭实现了AutoClosable接口的类,实现类需要实现close()方法。"try-with-resouce声明",将try-catch-finally简单化为:"try-catch",这其实是一种语法糖,在编译时会自动转化为:try-catch-finally语句。
try(将需要打开可能出现异常的东西放在这里面,可以放多个,用分号隔开){//使用
}catch(Exception e){e.prinStackTrace;
}
和捕获异常的比较
和上面的捕获异常比起来,简洁多了,不需要关闭,会自动关闭
自定义异常
-
在程序中,可能遇到JDK提供的任何标准异常类都无法充分的描述我们想要表达的问题,这种情况下我们可以自己创建自己的异常类,即自定义异常类。
-
自定义异常类只需从Exception类或者他的子类派生出一个子类即可
-
自定义异常类如果继承Exception类,那么则为受检察异常,必须对其进行处理,如果不想处理,可以让自定义异常继承运行时异常RuntimeException类
-
习惯上,自定义异常类包含2个构造器:一个是默认的构造器,另一个是带有详细信息的构造器
自定义异常类的使用
class IllegalAgeException extends Exception{public IllegalAgeException(){//默认构造器}public IllegalAgeException(String message){//带有详细信息的构造器,信息存储在message中super(message);}
}
如何利用百度搜索解决异常问题
正确开发和学习中,我们经常会遇到各种异常,遇到异常时,需要遵循以下四个步骤来解决:
-
细心察看异常信息,确定异常种类和相关Java代码行号,
-
确定上下文相关一些关键词信息(疑难问题,需要)
-
拷贝异常信息到百度,查看相关帖子,寻找解决思路
IDEA开发环境的调试视图
IDEA提供了非常方便的程序调试功能,进行调试的核心是设置断点时展示挂起,停止执行,就想看视频按下暂停键一样,我们可以详细的观看停止处的每一个细节。
飞机大战
建立游戏主窗口
package plane;import java.awt.Frame;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;/** ** 游戏主窗口 ** * **/public class MyGameFrame extends Frame { //初始化窗口 public void launchFrame(){ this.setTitle("飞机大战"); setVisible(true);//设置窗口可见 setSize(500, 500);//设置窗口大小 setLocation(100,100);//设置窗口打开的位置 //增加关闭窗口的动作事件 this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e){ System.exit(0); } }); } public static void main(String[] args) { MyGameFrame myGameFrame=new MyGameFrame(); myGameFrame.launchFrame(); System.out.println(); }}
图形和文本的绘制
-
paint方法
如果要在窗口中画图或者显示什么内容,我们需要重写paint(Graphics g),这个方法的作用是:画出整个窗口和内部内容。它会被系统自动调用,我们自己不需要去调用这个方法。
@ overridepublic void paint(Graphics g){ }
-
Graphics 画笔对象_画图形
我们可以把这个对象想象成一个画笔,窗口中的所有图形都是由这只画笔画出来的。
package plane;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/** *在主窗口中画图 ** * **/
public class MyGameFrame extends Frame {@Overridpublic void paint(Graphics g) {//把g当作是一支画笔// TODO Auto-generated method stubColor color=g.getColor();//笔是借来的,为了用完之后完璧归赵 g.setColor(new Color(255,0,0));//设置画笔的颜色g.drawLine(100, 100, 400, 400);//画一条线g.setColor(new Color(0,255,0));//设置画笔的颜色g.drawRect(100, 100, 300, 300);//画一个矩形g.setColor(new Color(0,0,255));//设置画笔的颜色g.drawOval(100, 100, 300, 300);//画一个椭圆g.setColor(new Color(0,255,255));//设置画笔的颜色g.drawString("iterator", 250, 250);//画一个字符串g.setColor(color);//用完了,完璧归赵} //初始化窗口 public void launchFrame(){this.setTitle("飞机大战"); setVisible(true);//设置窗口可见setSize(500, 500);//设置窗口大小 setLocation(100,100);//设置窗口打开的位置//增加关闭窗口的动作事件 this.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e){System.exit(0); } });
}
public static void main(String[] args) { MyGameFrame myGameFrame=new MyGameFrame();myGameFrame.launchFrame(); System.out.println();
}
}
ImageIO 实现图片加载技术
游戏开发中,图片加载是常见的技术,我们在此处使用ImageIO类实现图片加载技术,并且为了代码的复用,将图片加载的方法封装在GameUtil工具类中,便于以后直接调用。
GameUtil工具类
package plane;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.nio.Buffer;
import javax.imageio.ImageIO;
/*** * @author xiehongkai* 游戏的工具类**/
public class GameUtil {//构造器也可以私有·,防止别人对构造器的修改private GameUtil(){}public static Image getImage(String path){//Image/plane.jpgBufferedImage image=null;URL u=GameUtil.class.getClassLoader().getResource(path);try {image=ImageIO.read(u);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return image;}public static void main(String[] args) {Image image=GameUtil.getImage("Images/plane.jpg");System.out.println(image);}}
在窗口实现图片的绘画
package plane;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/****游戏主窗口*** **/
public class MyGameFrame extends Frame {Image plane=GameUtil.getImage("Images/plane.PNG");Image bg=GameUtil.getImage("Images/background.jpg");@Overridepublic void paint(Graphics g) {g.drawImage(bg, 0, 0, 500, 500,null);g.drawImage(plane, 200, 300, 100,100,null);}//初始化窗口public void launchFrame(){this.setTitle("飞机大战");setVisible(true);//设置窗口可见setSize(500, 500);//设置窗口大小setLocation(100,100);//设置窗口打开的位置//增加关闭窗口的动作事件this.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e){System.exit(0);}});}public static void main(String[] args) {MyGameFrame myGameFrame=new MyGameFrame();myGameFrame.launchFrame();System.out.println();}
}
多线程和内部类实现动画效果
package plane;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.nio.Buffer;
import javax.imageio.ImageIO;
/** * * @author xiehongkai * 游戏的工具类 * */
public class GameUtil { //构造器也可以私有·,防止别人对构造器的修改private GameUtil(){
} public static Image getImage(String path){//Image/plane.jpg BufferedImage image=null;URL u=GameUtil.class.getClassLoader().getResource(path);try { image=ImageIO.read(u); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return image;}public static void main(String[] args) {Image image=GameUtil.getImage("Images/plane.PNG");System.out.println(image);}
}
package plane;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/****游戏主窗口*** **/
public class MyGameFrame extends Frame {Image plane=GameUtil.getImage("Images/plane.PNG");Image bg=GameUtil.getImage("Images/background.jpg");int planex=200;int planey=100;private Image gameImage;private Graphics mGraphics;public MyGameFrame(){}//准备离屏图像private void parper(){
mGraphics.drawImage(bg, 0, 0, 1000, 1000,null);mGraphics.drawImage(plane,planex,planey, 100,100,null);}@Overridepublic void paint(Graphics g) {parper();g.drawImage(gameImage, 0, 0, null);planex +=1;planey +=1;}//初始化窗口public void launchFrame(){this.setTitle("飞机大战");setVisible(true);//设置窗口可见setSize(1000, 1000);//设置窗口大小setLocation(100,100);//设置窗口打开的位置//增加关闭窗口的动作事件this.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e){System.exit(0);}});new paintThread().start();//启动重画窗口的线程}/*** 定义了一个重画窗口的线程类* **/class paintThread extends Thread{@Overridepublic void run() {gameImage = createImage(1000, 1000);mGraphics = gameImage.getGraphics();// TODO Auto-generated method stubwhile(true){repaint();//内部类可以直接使用外部类的所有成员try {Thread.sleep(50);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}public static void main(String[] args) {MyGameFrame myGameFrame=new MyGameFrame();myGameFrame.launchFrame();System.out.println();}
}
泛型(Generics)
1.泛型的基本概念
泛型的本质就是“数据类型的参数化”,处理的的数据类型不是固定的,而是可以作为参数的传入,我们可以把泛型理解为一个占位符(类似:形式参数),即告诉编译器,在调用泛型时,必须传入实际类型。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
参数化类型,白话说就是:
-
把类型当作参数一样传递【不用强制转换】
-
<数据类型>只是引用类型
1.2泛型的好处
-
代码可读性好【不用强制转换】
-
程序更加安全,【只要编译时不出错,运行时期就不会出现ClasscastExceptions异常】
1.3类型的擦除
编译时采用泛型写的类型参数,编译器会在编译时去掉,这就称为“类型擦除”
泛型主要用于编译阶段编译后产生的字节码class文件不包含泛型的类型信息,涉及类型转换仍然是强制类型转换。
类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。
泛型主要是方便程序员的代码编写,以及更好的安全性检测
2.泛型的使用
2.1定义泛型
泛型字符可以是任何标识符,一般采用几个标记:E、T、K、V、N、?
泛型标记 | 对应单词 | 说明 |
---|---|---|
E | Element | 在容器中使用,表示容器中的元素 |
T | Type | 表示普通的JAVA类 |
K | Key | 表示键,例如Map中的Key键 |
V | Value | 表示值 |
N | Number | 表示数值类型 |
? | 表是不确定的JAVA类型 |
2.2泛型类
泛型类就是把泛型定义在类上,用户在使用该类的时候,才把类型明确下来,泛型类的具体使用方法是在类的名称后添加一个或多个类型参数声明,如:<T>,<T、K、V>
2.2.1语法结构
public class 类名<泛型表示符号>{}
举例
//泛型类的定义
package myjava;
public class Generics<T> {private T flag;public void setflag(T flag){//没有返回类型this.flag=flag;}public T getflag(){//有返回类型return this.flag;}
}
//调试
package myjava;
public class Test {public static void main(String[] args) {Generics<String> generics=new Generics<>();//使用该类时,把类型确定下来generics.setflag("nihfuf");String str=generics.getflag();System.out.println(str);Generics<Integer> generics2=new Generics<>();generics2.setflag(100);Integer a=generics2.getflag();System.out.println(a);}
}
2.3 泛型接口
泛型接口和泛型类的声明方式一致,泛型接口的具体类型需要在实现类中进行声明。
2.3.1语法结构
public interface 接口名<泛型表示符号>{}
举例
//定义接口类
package myjava;
public interface Igeneric <T>{T getName(T name);
}
//实现接口类
package myjava;
public class IgenericImpl implements Igeneric<String>{//在实现接口的时候就给定具体类型
@Overridepublic String getName(String name) {// TODO Auto-generated method stubreturn name;}
}
//调试
package myjava;
public class Demo {public static void main(String[] args) {IgenericImpl igenericImpl=new IgenericImpl();//用接口实现类来修饰String name=igenericImpl.getName("本地环境");System.out.println(name);Igeneric<String> igeneric=new IgenericImpl();//用接口来实现修饰,需给定具体类型String name1=igeneric.getName("外地环境");System.out.println(name1);}
}
2.4泛型方法
泛型类中定义的泛型,在方法中也可以使用,但是,我们经常要仅仅在一个方法上使用泛型,这个时候可以使用泛型方法。
泛型方法是指将方法的参数类型定义成泛型,以便在调用时接受不同类型的参数,类型参数可以有多个,用逗号隔开。如:<k、T>,定义时,类型参数一般放在返回值前面。
调用泛型方法时,不需要像泛型类那样告诉编译器什么类型,编译器可以自动推断出类型来。
2.4.1非静态方法
2.4.1.1语法结构
public <泛型表示符号>void getName(泛型表示符号 name){}
或
public <泛型表示符号>泛型表示符号 getName(泛型表示符号 name){}
举例
package MyJava;
public class MethodGeneric {public <T> void setName(T name){System.out.println(name);}public <T> T getName(T name){return name;}public static void main(String[] args) {MethodGeneric methodGeneric=new MethodGeneric();methodGeneric.setName("张筑港");//不用告诉编译器是什么类型的,它会自动推断出来String string=methodGeneric.getName("fdshghih");//强制类型转换System.out.println(string);}
}
2.4.2静态方法
静态方法中使用泛型时有一种情况需要注意一下,那就是静态方法无法访问类上定义的泛型,如果静态方法操作时引用数据类型不确定的时候,必须将泛型定义在方法上。
2.4.2.1语法结构
public static <泛型表示符号>void getName(泛型表示符号 name){}
或
public static <泛型表示符号>泛型表示符号 getName(泛型表示符号 name){}
举例
package MyJava;
public class MethodGeneric {public static <T> void setFlag(T flag){System.out.println(flag);}public static <T> T getFlag(T flag){return flag ;}public static void main(String[] args) {MethodGeneric.setFlag(100);//静态方法是不需要进行实例化的,直接用类名调用即可String string=MethodGeneric.getFlag("nfgdgn");//强制类型转换System.out.println(string);}
}
2.4.3 泛型方法与可变参数
在泛型方法中,反省也可以定义可变参数类型。
2.4.3.1 语法结构
public <泛型表示符号> void showMsg(泛型表示符号...agrs){}
举例
package MyJava;
public class MethodGeneric {public <T> void method(T...args){for(T t:args){//遍历数组System.out.println(t);}}public static void main(String[] args) {MethodGeneric methodGeneric=new MethodGeneric();String[] arr=new String[]{"a","b","c","d"};Integer[] arr1=new Integer[]{1,2,2,3,4,6};methodGeneric.method(arr);methodGeneric.method(arr1);}
}
2.5 通配符和上下界定
2.5.1无界通配符
“?”表示类型通配符,用于代替具体的类型,它只能在“<>”中使用,可以解决当类型不确定时的问题。
2.5.1.1语法结构
public void showFlag(Generic<?> generic){}
举例
package MyJava;
public class MethodGeneric<T> {private T flag;public void setFlag(T flag){this.flag=flag;}public T getFlag(){return flag;} }
package MyJava;
public class ShowMsg {public void showFlag(MethodGeneric<?> methodGeneric){//用于输出MethodGeneric类当中的flag值System.out.println(methodGeneric.getFlag());}
}
//调试
package MyJava;
public class Test {public static void main(String[] args) {ShowMsg showMsg=new ShowMsg();MethodGeneric<Integer> methodGeneric=new MethodGeneric<>();//定义一个Integer类型的methodGeneric.setFlag(200);showMsg.showFlag(methodGeneric);MethodGeneric<Number> methodGeneric2=new MethodGeneric<>();//定义一个Number类型的methodGeneric2.setFlag(200);showMsg.showFlag(methodGeneric2);MethodGeneric<String> methodGeneric3=new MethodGeneric<>();//定义一个String类型的methodGeneric3.setFlag("jfvjkkjdv");showMsg.showFlag(methodGeneric3);}
}
2.5.2通配符的上限界定
上限界定表示通配符的类型T类以及T类的子类或者时T接口以及T接口的子接口,该方式同样适用于与泛型的上限界定。
2.5.2.1语法结构
public void showFlag(Generic<? extends Number>) generic{}
举例
package MyJava;
public class MethodGeneric<T> {private T flag;public void setFlag(T flag){this.flag=flag;}public T getFlag(){return flag;} }
package MyJava;
public class ShowMsg {//对通配符的上限界定,如下只能是Number类型或其子类类型public void showFlag(MethodGeneric<? extends Number> methodGeneric){//用于输出MethodGeneric类当中的flag值System.out.println(methodGeneric.getFlag());}
}
package MyJava;
public class Test {public static void main(String[] args) {ShowMsg showMsg=new ShowMsg();MethodGeneric<Integer> methodGeneric=new MethodGeneric<>();//定义一个Integer类型的methodGeneric.setFlag(200);showMsg.showFlag(methodGeneric);MethodGeneric<Number> methodGeneric2=new MethodGeneric<>();//定义一个Number类型的methodGeneric2.setFlag(200);showMsg.showFlag(methodGeneric2);MethodGeneric<String> methodGeneric3=new MethodGeneric<>();//定义一个String类型的methodGeneric3.setFlag("jfvjkkjdv");showMsg.showFlag(methodGeneric3);}
}
//其中String类型会报错,因为String类型不是Number的子类
// Integer类型不会报错,因为Integer时Number类型的子类
2.5.3通配符的下线界定
下限限定指的是通配符的类型是T类以及T类的父类或者T接口以及T接口的父接口。
注意:该接口不适于泛型类。
2.5.3.1 语法结构
public void showFlag(Generic<? super Integer> generic){
}
举例
package MyJava;
public class MethodGeneric<T> {private T flag;public void setFlag(T flag){this.flag=flag;}public T getFlag(){return flag;} }
package MyJava;
public class ShowMsg {//对通配符的下限界定,如下只能是Number类型或其父类类型public void showFlag(MethodGeneric<? super Integer> methodGeneric){//用于输出MethodGeneric类当中的flag值System.out.println(methodGeneric.getFlag());}
}
package MyJava;
public class Test {public static void main(String[] args) {ShowMsg showMsg=new ShowMsg();MethodGeneric<Integer> methodGeneric=new MethodGeneric<>();//定义一个Integer类型的methodGeneric.setFlag(200);showMsg.showFlag(methodGeneric);MethodGeneric<Number> methodGeneric2=new MethodGeneric<>();//定义一个Number类型的methodGeneric2.setFlag(200);showMsg.showFlag(methodGeneric2);MethodGeneric<String> methodGeneric3=new MethodGeneric<>();//定义一个String类型的methodGeneric3.setFlag("jfvjkkjdv");showMsg.showFlag(methodGeneric3);}
}
//其中String类型会报错,因为String类型不是Integer的父类
// Number类型不会报错,因为Number类型是Integer类的父类
泛型的总结
泛型主要用于编译阶段,编译后生成的字节码class文件不包含泛型中的类型信息,类型参数在编译后或被替换成Object,运行时虚拟机并不知道泛型,因此使用泛型时,如下几种情况是错误的,
-
基本类型不能用于泛型
Test<int>t;这种写法是错误的,我们可以使用对应的包装类Test<Integer>t;
-
不能通过类型参数创建对象
T emp=new T();运行时参数T会被替换成Object,无法创建T类型的对象,容易引起误解,所以在Java中不支持这种写法。
容器
1.容器的介绍
容器就是用来容纳物体,管理物体。生活中,我们会用到各种各样的容器,如锅碗瓢盆,箱子和包等。程序中的容器也有类似的功能,用来容纳和管理数据,比如新闻网站的新闻列表、教育站的课程列表就是用容器来管理的。视频课程信息也是用容器来管理的。
事实上,我们熟知的数组就是一种容器,但是数组操作起来非常的不方便。
2.容器的结构
2.1 结构图
2.2单例集合
就是将数据一个一个的进行存储。
2.3双例集合
基于Key和Value的结构存储数据。
3.单例集合的使用
3.1Colection接口的介绍
collection是单例集合的根接口,他是集中和收集的意思。
3.1.1Collection接口中的抽象方法
方法 | 说明 |
---|---|
boolean add(Object element) | 增加元素到容器中 |
boolean remove(Object element) | 从容器中移除元素 |
int size() | 容器中元素的数量 |
boolean isEmpty() | 容器是否为空 |
void clear() | 清除容器中所有元素 |
Iterator iterator() | 获得迭代器,用于遍历所有元素 |
boolean containsAll(colection c) | 本容器是否包含c容器中的所有元素 |
boolean addAll(colection c) | 将容器c中所有元素增加到本容器,集合的并集 |
boolean removeAll(colection c) | 移除本容器和容器c中都包含的所有元素,集合中的差集 |
boolean retainAll(colection c) | 去本容器和容器c中都能包含的元素,移除非交集元素,集合中的交集 |
Object[] toArray() | 转化成Object数组 |
removeIf | 作用是删除容器中所有满足filter指定条件的元素 |
stream parallelStream | stream和parallelStream分别返回该容器的Stream视图表示,不同之处在parallelStream()返回并行的Stream,Stream是Java函数式编程的核心类 |
spliterator | 可分割的迭代器,不同以往的iterator需要顺序迭代,Spliterator可以分割为若干个小的迭代器进行并行操作,可以实现多线程操作提高效率。 |
3.2List接口的介绍
3.2.1 List接口的特点
有序:有序(元素存入集合的顺序和去出的顺序一致)。List中每个元素都有索引标记,可以根据索引标记访问元素,从而精确控制这些元素。
可重复:List允许加入重复的元素,更确切地讲,List通常允许满足e1.equals(e2)的元素重复加入容器。
3.2.2List的常用方法
方法 | 说明 |
---|---|
void add(int index,Object element) | 在指定位置插入元素,以前的从这个位置到后面的的元素全部后移一位 |
Object set(int index,Object element) | 修改指定位置的元素 |
Object get(int index) | 返回指定位置的元素 |
Object remove(int index) | 删除指定位置的元素,后面元素全部前移一位 |
int indexOf(Object o) | 返回第一个匹配元素的索引,如果没有该元素,返回-1 |
int LastIndexOf(Object o) | 返回最后一个匹配元素的索引,如果没有该元素,返回-1 |
3.3 ArrayList 容器类
ArrayList是List接口的实现类,是List存储特征的具体实现。
ArrayList底层是用数组实现的存储,特点:查询效率高、增删效率低、线程不安全。
3.3.1添加元素
package MyJava;
import java.util.List;
import java.util.ArrayList;
public class ArrayListTest {public static void main(String[] args) {//实例化ArrayList容器List<String> list=new ArrayList<>();//List类是一个泛型类//添加元素boolean flag=list.add("bjsxt");//有返回值boolean flag1=list.add(100);//报错,只能是String类型System.out.println(flag);list.add(1, "element");//无返回值,添加的位置不能大于添加的元素的个数,//前面我们就添加一个元素,所以只能为1或0}
}
3.3.2 获取元素
E get(int index)//返回此列表中指定的元素
异常情况:IndexOutOfZBoundsException - 如果超出索引范围( index<0| |index>=size )
package MyJava;
import java.util.List;
import java.util.ArrayList;
public class ArrayListTest {public static void main(String[] args) {//实例化ArrayList容器List<String> list=new ArrayList<>();//List类是一个泛型类//添加元素boolean flag=list.add("bjsxt");//有返回值boolean flag1=list.add("NIZICHIMA");//报错,只能是String类型list.add(1, "element");list.add(2,"nihao");//无返回值,添加的位置不能大于添加的元素的个数,//前面我们就添加一个元素,所以只能为1或0System.out.println(list.get(0));System.out.println(list.get(1));System.out.println(list.get(2));System.out.println(list.get(3));System.out.println(list.get(4));//超出了索引值范围,报错}
}
运行结果:bjsxtelementnihaoNIZICHIMAException in thread "main" java.lang.IndexOutOfBoundsException: Index: 4, Size: 4at java.util.ArrayList.rangeCheck(ArrayList.java:657)at java.util.ArrayList.get(ArrayList.java:433)at MyJava.ArrayListTest.main(ArrayListTest.java:23)
用遍历的方法来实现对列表中的元素的获取
package MyJava;
import java.util.List;
import java.util.ArrayList;
public class ArrayListTest {public static void main(String[] args) {//实例化ArrayList容器List<String> list=new ArrayList<>();//List类是一个泛型类//添加元素boolean flag=list.add("bjsxt");//有返回值boolean flag1=list.add("NIZICHIMA");//报错,只能是String类型list.add(1, "element");list.add(2,"nihao");//无返回值,添加的位置不能大于添加的元素的个数,//前面我们就添加一个元素,所以只能为1或0//用for循环来遍历列表里的元素for(int i=0;i<list.size();i++){System.out.println(list.get(i));}}
}
3.3.3 删除元素
-
根据索引删除元素,E remove(int index);{在List当中所定义的}
-
删除指定元素,Boolean remove(Object o);{在Collection当中所定义的}
3.3.4 替换元素
E set(int index,E element);
3.3.5 清空容器
void clear();
package MyJava;
import java.util.List;
import java.util.ArrayList;
public class ArrayListTest {public static void main(String[] args) {//实例化ArrayList容器List<String> list=new ArrayList<>();//List类是一个泛型类//添加元素boolean flag=list.add("bjsxt");//有返回值boolean flag1=list.add("NIZICHIMA");//报错,只能是String类型list.add(1, "element");list.add(2,"nihao");//无返回值,添加的位置不能大于添加的元素的个数,//前面我们就添加一个元素,所以只能为1或0//用for循环来遍历列表里的元素for(int i=0;i<list.size();i++){System.out.println(list.get(i));}System.out.println("------------清空容器-----------");list.clear();System.out.println(list.size());}
}
//运行结果
bjsxt
element
nihao
NIZICHIMA
------------清空容器-----------
0
3.3.6 判断容器是否为空
Boolean isEmpty();
3.3.7 判断容器中是否包含指定元素
Boolean contains(Object o);
3.3.8 查找元素的位置
-
查找元素第一次出现的位置:int indexOf(Object o);
-
查找元素最后一次出现的位置:int lastIndexOf(Object o);
3.3.9 将单例集合转换成数组
-
转换为Object数组:object[] toArray();
返回一个数组,其中包含所有的元素在这个列表中适当顺序(从第一个元素到最后一个元素);
注意:Java里面只能对一个对象强转,不能对一个数组强转。
package MyJava; import java.util.List; import java.util.ArrayList; public class ArrayListTest {public static void main(String[] args) {//实例化ArrayList容器List<String> list=new ArrayList<>();//List类是一个泛型类//添加元素boolean flag=list.add("bjsxt");//有返回值boolean flag1=list.add("NIZICHIMA");//报错,只能是String类型list.add(1, "element");list.add(2,"nihao");//无返回值,添加的位置不能大于添加的元素的个数,//前面我们就添加一个元素,所以只能为1或0//用for循环来遍历列表里的元素for(int i=0;i<list.size();i++){System.out.println(list.get(i));}System.out.println("------------将单例集合转换成数组-------------");//将ArrayList转换为Object[]Object[] arr=list.toArray();//不能String[] arr=(String[])list.toArray();for(int j=0;j<arr.length;j++){String str=(String) arr[j];//强转为String类型元素System.out.println(arr[j]);}} }
-
转换为泛型数组:<T>T[] toArray(T[] a); {---此方式不需要强转----}
package MyJava;
import java.util.List;
import java.util.ArrayList;
public class ArrayListTest {public static void main(String[] args) {//实例化ArrayList容器List<String> list=new ArrayList<>();//List类是一个泛型类//添加元素boolean flag=list.add("bjsxt");//有返回值boolean flag1=list.add("NIZICHIMA");//报错,只能是String类型list.add(1, "element");list.add(2,"nihao");//无返回值,添加的位置不能大于添加的元素的个数,//前面我们就添加一个元素,所以只能为1或0//用for循环来遍历列表里的元素for(int i=0;i<list.size();i++){System.out.println(list.get(i));}System.out.println("------------将单例集合转换成数组-------------");//将ArrayList转换为泛型数组String[] arr1=list.toArray(new String[list.size()]);for(int j=0;j<arr1.length;j++){System.out.println(arr1[j]);}}
}
3.3.10 容器的并集操作
boolean addAll(colection <? extends E> c); //集合包含元素被添加到C列表。
package MyJava;
import java.util.List;
import java.util.ArrayList;
public class ArrayListTest {public static void main(String[] args) {List<String> aList=new ArrayList<>();aList.add("a");aList.add("b");aList.add("c");aList.add("f");List<String> bList=new ArrayList<>();bList.add("b");bList.add("c");bList.add("d");bList.add("e");boolean flag=aList.addAll(bList);//谁并就得遍历谁。System.out.println(flag);//为我们返回一个boolean值,看是否成功for(String str:aList){//遍历我们得出的并集元素System.out.println(str);}}
}
3.3.11 容器的交集操作
Boolean retainAll(colection<? > c);package MyJava;
import java.util.List;
import java.util.ArrayList;
public class ArrayListTest {public static void main(String[] args) {List<String> aList=new ArrayList<>();aList.add("a");aList.add("b");aList.add("c");aList.add("f");List<String> bList=new ArrayList<>();bList.add("b");bList.add("c");bList.add("d");bList.add("e");boolean flag=aList.retainAll(bList);System.out.println(flag);for(String str:aList){System.out.println(aList);}}
}
3.3.12 容器的差集操作
boolean removeAll(colection <?> c);package MyJava;
import java.util.List;
import java.util.ArrayList;
public class ArrayListTest {public static void main(String[] args) {List<String> aList=new ArrayList<>();aList.add("a");aList.add("b");aList.add("c");aList.add("f");List<String> bList=new ArrayList<>();bList.add("b");bList.add("c");bList.add("d");bList.add("e");boolean flag=aList.removeAll(bList);System.out.println(flag);for(String str:aList){System.out.println(aList);}}
}
3.3.13ArrayList源码分析
3.3.13.1Arraylist 底层存储方式
ArrayList底层是用数组实现的存储
Ctrl+鼠标点击自己去看去吧。
3.4 Vector容器类
Vectors底层是用数组实现的,相关的方法都加了同步检查,因此线程安全,但是效率低。比如:indexOf就添加了synchronized同步标记。
3.4.1 Vector的使用
Vector和ArrayList是相同的,因此它们都实现了List接口,对List抽象方法作了具体实现。
3.4.2 Stack容器
stack栈容器是Vector类的一个子类,它实现了一个标准的后进先出的栈。
3.4.2.1 stack的特点
后进先出,它通过五个操作方法对Vector进行扩展,允许将向量视为堆栈。
3.4.2.2 操作栈的方法
Modifier and Type | Method and Description |
---|---|
boolean | empty() 测试如果这个栈是空的 |
E | peek() 看看这个栈堆的顶部对象,没有从栈堆中删除它 |
E | pop() 删除这个堆栈的顶部对象,并返回该对象的值函数 |
E | push(E item) 把一个元素添加到到栈堆的顶部 |
int | search(object o) 返回基于位置的对象在这个栈堆 |
package MyJava;
import java.util.Stack;
public class Stacktest {public static void main(String[] args) {//实例化栈容器Stack<String> stack=new Stack<>();//将元素添加到栈容器中stack.push("a");stack.push("b");stack.push("c");stack.push("d");//获取栈容器中的元素stack.pop();//从d开始获取//判断栈是否为空boolean flag=stack.empty();System.out.println(flag);//查看栈顶元素String string=stack.peek();System.out.println(string);//返回元素在栈容器中的位置int a=stack.search("b");System.out.println(a);}
}
3.4.2.3 Stack 的使用范例
判断元素的对称性:
String str="...{.....[....(....)...]....}..(....)..[...]...";
package MyJava;
import java.util.Stack;
public class Stacktest {public static void main(String[] args) {System.out.println("------------------------------");Stacktest stacktest=new Stacktest();stacktest.symmetry();}//匹配符号的对称性public void symmetry(){String str="...{.....[....(....)...]....}..(....)..[...]...";//实例化StackStack<String> stack=new Stack<>();//假设修正法boolean flag=true;//假设是匹配的//拆分字符串获取字符for(int i=0;i<str.length();i++){char c=str.charAt(i);if(c=='{'){stack.push("}");}if(c=='['){stack.push("]");}if(c=='('){stack.push(")");}//判断符号是否匹配if(c=='}'||c==']'||c==')'){if(stack.empty()){//如果栈里面没有添加字符为空flag=false;break;}String xString=new String();//如果栈不为空,已经在前面添加过数据if(xString.charAt(0) !=c){flag=false;break;}}}if(! stack.empty()){//假如有没有匹配对象的字符flag=false;}System.out.println(flag);}
}
3.5 LinkedList 容器类
LinkedList容器类底层是用双链表实现的存储,特点:查询效率低,增删效率高、线程不安全。
双向链表也称为双链表,是链表的一种,他的每个数据节点都有两个数据指针,分别指向前一个节点和后一个节点,所以,从双链表的任意一个节点开始,都可以很方便的找到所有节点。
3.5.1 双向链表的介绍
class Node<E>{E item;Node<E> next;Node<E> prev;
}
3.5.2 LinkedList的使用(List标准)
LinkedList实现了List接口,所以:LinkedList是具备List的存储特征的(有序,元素有重复)。
3.5.3 LinkedList的使用(非List标准)
方法 | 说明 |
---|---|
void addFirst(E e) | 将指定的元素插入到开头 |
void addLast(E e) | 将指定元素插入到结尾 |
getFirst() | 返回此列表的第一个元素 |
getLast() | 返回此列表的最后一个元素 |
removeFirst() | 移除此列表的第一个元素,并返回这个元素 |
removeLast() | 移除此列表的最后一个元素,并返回这个元素 |
E pop() | 从此列表所表示的堆栈处弹出一个元素,等效于removeLast() |
void push(E e) | 将元素推入此列表所表示的堆栈,等效于addFirst)(E e) |
boolean isEmpty() | 判断此列表是否包含元素,如果不包含元素则返回ture |
package MyJava;
import java.util.LinkedList;
public class LinkedListTest {public static void main(String[] args) {LinkedList<String> linkedList=new LinkedList<>();//在头部添加元素linkedList.addFirst("a");linkedList.addFirst("b");linkedList.addFirst("c");for(String str:linkedList){System.out.println(str);}//在尾部添加元素linkedList.addLast("d");linkedList.addLast("e");linkedList.addLast("f");for(String str2:linkedList){System.out.println(linkedList);}//返回第一个元素 System.out.println(linkedList.getFirst());//删除第一个元素System.out.println(linkedList.removeFirst());for(String str3:linkedList){System.out.println(linkedList);}//从堆栈列表中弹出一个元素System.out.println(linkedList.pop());for(String str4:linkedList){System.out.println(linkedList);}//判断堆栈是否为空boolean x=linkedList.isEmpty();System.out.println(x);}
}
3.5.4 LinkedList源码分析
Ctrl+鼠标
3.6 Set 接口的介绍
Set接口继承自Colection,Set中没有增加新方法,我们在前面学习的List方法,在Set中仍然适用。
3.6.1 Set接口的特点
特点:无序、不可重复。无序是指在Set中元素没有索引,我们只能遍历查找,不可重复是指不可加入重复的元素,更确切地讲:新元素如果和Set中的元素通过equals()方法对比为true,则只能保留一个。
Set常用的实现类有:HashSet、TreeSet等,我们一般使用HashSet。
3.6.2HashSet容器类
HashSet是一个没有重复元素的集合,不保证元素的顺序,而且hashSet允许有null元素,HashSet是采用哈希算法实现,底层实际上是用HashMap实现的,HashSet本质上就是简化版的HashMap.因此,查询效率和增删效率都比较高。
3.6.2.1 Hash算法原理
Hash算法也称散列算法,下面是一个简单的哈希算法案例
模9运算:有一堆数,然后有一个空间足够大的栈,把这些数分别除以模9,得到的值放入对应下标的格子里面,遇到重复的放入下一个格子。
解析:除以9取余,以余数对应的索引值放入数据。从上面数据可以知道,当我们将24模9的时候会发现,6的这个位置有15这个数值了,那么把24这个数值往后挪一位。
3.6.2.2 HashSet的使用
package MyJava;
import java.util.Collection;
import java.util.HashSet;
public class HashSettest {public static void main(String[] args) {//实例化HashSetCollection<String> collection=new HashSet<>();//collection和Set都可以//添加元素collection.add("a");collection.add("b");collection.add("c");collection.add("d");collection.add("a");//获取元素,在collection和Set中容器中没有索引,所以没有对应的get(int index)方法for(String str:collection){//可以通过遍历来获取元素,获取的元素没有重复//的,并且顺序不定System.out.println(collection);}//删除元素boolean flag=collection.remove("a");System.out.println(flag);for(String str:collection){//可以通过遍历来获取元素,获取的元素没有重复//的,并且顺序不定System.out.println(collection);}}
}
反正就是Collection接口里面的所有方法HashSet类都实现了,直接用。
3.6.2.3 HashSet存储特征分析
HashSet是一个不保证元素的顺序且没有重复元素的集合,是线程不安全的。HashSet允许有null元素。
-
无序:
在HashSet中底层是使用HashMap存储元素的,HashMap底层使用的是数组与链表实现元素的存储,元素在数组中存储时,并不是有序存放的也不是随机存放的,而是对元素的哈希值进行运算决定元素在数组中的位置。
-
不重复:
当两个元素的哈希值进行运算后得到数组和中相同的位置的时候,会调用元素的equals()方法来判断两个元素是否相同,如果元素相同则不会添加该元素,如果不相同,则会单向链表来保存该元素。
3.6.2.4 通过HashSet存储自定义对象
3.6.2.4.1 创建Users对象
3.6.2.4.2 在HashSet中存储Users对象
package MyJava;
public class Users {private String username;private int userage;public Users(String username,int userage) {this.userage=userage;this.username=username;}public Users() {// TODO Auto-generated constructor stub}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public int getUserage() {return userage;}public void setUserage(int userage) {this.userage = userage;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn "Users{"+"username:"+username+"userage:"+userage+"}"; }@Overridepublic boolean equals(Object obj) {System.out.println("equals...");if(this==obj) return true;if(obj==null||getClass() !=obj.getClass()) return false;Users users=(Users)obj;if(userage !=users.userage) return false;return username !=null?username.equals(users.username):null;}@Overridepublic int hashCode() {int result=username !=null ?username.hashCode():0;result=31*result+userage; return result;}
}
package MyJava;
import java.util.HashSet;
import java.util.Set;
public class HashSettest {public static void main(String[] args) {Set<Users> set=new HashSet<>();Users u=new Users("laoxie",18);Users u1=new Users("laoxie",18);set.add(u);set.add(u1);System.out.println(u.hashCode());//u的哈希码System.out.println(u1.hashCode());//u1的哈希码//它们存放的位置是不同的,哈希码不相同for(Users users:set){System.out.println(users);}}
}
3.6.2.5 HashSet底层源码分析
CTRL+鼠标
3.6.3 TreeSet容器类
treeset是一个可以对元素进行排序的容器,底层实际上是用TreeMap实现的。内部维持了一个简化版的TreeMap,通过Key来存储Set的元素,TreeSet内部需要对存储的元素进行排序,因此我们需要给定排序规则。
排序规则实现方式:
-
通过元素自身实现比较规则
-
通过比较器指定比较规则
3.6.3.1 TreeSet的使用
package MyJava;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest {public static void main(String[] args) {//实例化TreeSetSet<String> set=new TreeSet<>(); //添加元素set.add("a");set.add("d");set.add("c");set.add("b");//获取元素for(String str1:set){//可以看出遍历的时候对字符进行了排序System.out.println(str1);}}
}
3.6.3.2 通过元素自身实现比较规则
在元素自身实现比较规则时,需要实现comparable接口中的comparaTo方法,该方法中用来定义比较规则,TreeSet通过调用该方法来对元素的排序处理。
创建Users类,在TreeSet中存放users对象。
package MyJava;
public class Users implements Comparable<Users>{private String username;private int userage;public Users(String username,int userage) {this.userage=userage;this.username=username;}public Users() {// TODO Auto-generated constructor stub}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public int getUserage() {return userage;}public void setUserage(int userage) {this.userage = userage;}@Overridepublic String toString() {return "Users{"+"username:"+username+"userage:"+userage+"}"; }@Overridepublic boolean equals(Object obj) {System.out.println("equals...");if(this==obj) return true;if(obj==null||getClass() !=obj.getClass()) return false;Users users=(Users)obj;if(userage !=users.userage) return false;return username !=null?username.equals(users.username):null;}@Overridepublic int hashCode() {int result=username !=null ?username.hashCode():0;result=31*result+userage; return result;}@Overridepublic int compareTo(Users o) {// TODO Auto-generated method stubif(this.userage>o.getUserage()){return 1;}if(this.userage==o.getUserage()){return this.username.compareTo(o.getUsername());}return -1;}
}
package MyJava;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest {public static void main(String[] args) {//实例化TreeSetSet<Users> set1=new TreeSet<>();Users u1=new Users("xiaoxie",20);Users u2=new Users("xiaoming",22);Users u3=new Users("xiaojie",22);set1.add(u1);set1.add(u2);set1.add(u3);for(Users users:set1){System.out.println(users);}}
}
3.6.3.3 通过比较器来实现比较规则
通过比较器实现比较规则时,我们需要单独创建一个比较器,比较器需要实现Comparator接口中的compare方法来定义比较规则,在实例化TreeSet时将比较器对象交给TreeSet来完成对元素的排序处理,此时元素自身就不需要实现比较规则了。
创建比较器、创建Student类、在TreeSet中存储Users对象
package MyJava;
public class Student {private String name;private int age;public Student(String name,int age) {// TODO Auto-generated constructor stubthis.name=name;this.age=age;}public Student() {// TODO Auto-generated constructor stub}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;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn "Name:"+name+""+"Age:"+age;}
}
package MyJava;
import java.util.Comparator;
public class StudentCompara implements Comparator<Student>{//比较器//定义比较规则 @Overridepublic int compare(Student o1, Student o2) {// TODO Auto-generated method stubif(o1.getAge()>o2.getAge()){return 1;}if(o1.getAge()==o2.getAge()){return o1.getName().compareTo(o2.getName());}return -1;}
}
package MyJava;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest {public static void main(String[] args) {//实例化TreeSetSet<Student> set1=new TreeSet<>(new StudentCompara());Student s1=new Student("a1",19);Student s2=new Student("a2",10);Student s3=new Student("a3",18);set1.add(s1);set1.add(s2);set1.add(s3);for(Student student:set1){System.out.println(student);}}
}
3.6.3.4 TreeSet 底层源码分析
Ctrl+鼠标:
3.7 单例集合使用案例
需求:
产生1~10之间的随机数[1,10]闭区间,将不重复的是个随机数放到容器当中。
-
使用List容器类来实现:
package MyJava;
import java.util.ArrayList;
import java.util.List;
import javax.swing.text.StyledEditorKit.ForegroundAction;
public class ListDemo {public static void main(String[] args) {List<Integer> list=new ArrayList<>();while(true){//产生随机数int num=(int)(Math.random()*10+1);//判断是当前元素在容器当中是否存在if(! list.contains(num)){list.add(num);}//结束循环if(list.size()==10){break;}}for(Integer i:list){System.out.println(i);}}
}
-
使用Set容器类来实现:
package MyJava;
import java.util.Collection;
import java.util.HashSet;
public class SetDamo {public static void main(String[] args) {Collection<Integer> collection=new HashSet<>();while(true){int num=(int)(Math.random()*10+1);//添加元素collection.add(num);//结束循环if(collection.size()==10){break;}}for(Integer integer:collection){System.out.println(integer);}}}
//并没有作排序
4.双例集合的使用
4.1 Map接口介绍
4.1.1 Map接口的特点
Map接口定义了双例集合的存储特征,它并不是Collection接口的子接口,双里集合的存储特征是以Key和Value结构为单位进行存储,体现的是数学中的y=f(x)概念。
4.1.2 Map和Collection的区别
-
Collection中的容器,元素是孤立存在的,理解为单身,向集合中中存储元素采用一个元素的形式来存储
-
Map中的容器,元素是成对存在的,可以理解为现代的夫妻,每个元素由键和值来两部分组成,通过键可以找到对应的值
-
Collection中的容器时单例集合,Map中的集合是双例集合
-
Map中的集合不能包含重复的键,但是可以包含重复的值,每个键只能对应一个值。
-
Map中常用的容器有HashMap和TreeMap等。
4.1.3 Map中常用的方法
方法 | 说明 |
---|---|
V put(k key,v value) | 把key和Value添加到Map集合中 |
void putAll(Map m) | 从指定Map中将所有映射关系复制到此Map中 |
V remove(Object key) | 删除key对应的值value |
V get(Object key) | 根据指定的key获取对应的value值 |
boolean containskey(object key) | 判断指定容器中是否包含指定的key |
boolean containsValue(object value) | 判断容器中是否包含指定的Value |
Set keySet() | 获取Map中的所有的key,存储到Set集合中 |
Set<Map.Entry<k,v>> entrySet() | 返回一个Set基于Map.Entry类型包含Map中所有的映射 |
void clear() | 删除Map中所有的映射 |
Collection<V>values() | 获取集合中所有值的集合 |
int size() | 返回键值对个数 |
4.2 HashMap容器类
HashMap时Map接口的实现类,它采用的是哈希算法实现,是Map接口最常用的实现类。由于底层采用了哈希表存储数据,所以请要求键不能重复,如果发生重复,新的只会替换旧的值,HashMap在查看,删除,修改方面都有非常高的效率。
4.2.1 添加元素
package MyJava;
import java.util.HashMap;
import java.util.Map;
public class HashMapTest {public static void main(String[] args) {//实例化HashMap容器Map<String, String> map=new HashMap<>();//添加元素
// String Value=map.put("A", "nihao");//需覆盖之后才能返回key值
// System.out.println(Value);//输出结果为nullmap.put("A", "hello");String Value=map.put("A", "nihao");//需覆盖之后才能返回key值System.out.println(Value);//输出结果为hello}
}
4.2.2 获取元素
-
方式一:通过get()方法获取
package MyJava; import java.util.HashMap; import java.util.Map; public class HashMapTest {public static void main(String[] args) {//实例化HashMap容器Map<String, String> map=new HashMap<>();//添加元素 // String Value=map.put("A", "nihao");//需覆盖之后才能返回key值 // System.out.println(Value);//输出结果为nullmap.put("A", "hello");String Value=map.put("A", "nihao");//需覆盖之后才能返回key值System.out.println(Value);//输出结果为helloString str1=map.get("A");System.out.println(str1);//输出结果为nihao} }
-
方式二:通过keySet()方法来获取
package MyJava; import java.util.HashMap; import java.util.Map; import java.util.Set; public class HashMapTest {public static void main(String[] args) {//实例化HashMap容器Map<String, String> map=new HashMap<>();//添加元素map.put("A", "a");map.put("B", "b");map.put("C", "c");map.put("D", "d");map.put("E", "e");//获取元素//可以使用get()和keySet()方法一并完成Set<String> keys=map.keySet();for(String key:keys){String vl=map.get(key);System.out.println(key+"-----"+vl);}} }
-
方式三:通过entrySet()方法来获取Map.entry类型来获取元素
package MyJava;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapTest {public static void main(String[] args) {//实例化HashMap容器Map<String, String> map=new HashMap<>();//添加元素map.put("A", "a");map.put("B", "b");map.put("C", "c");map.put("D", "d");map.put("E", "e");//获取元素Set<Map.Entry<String, String>> entryset=map.entrySet();for(Map.Entry<String, String> entry:entryset){String key=entry.getKey();String value=entry.getValue();System.out.println("Key:"+key+"-----"+"value:"+value);}}
}
//结果
Key:A-----value:a
Key:B-----value:b
Key:C-----value:c
Key:D-----value:d
Key:E-----value:e
4.2.3 Map容器的并集操作
package MyJava;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapTest {public static void main(String[] args) {//实例化HashMap容器Map<String, String> map=new HashMap<>();//添加元素map.put("A", "a");map.put("B", "b");map.put("C", "c");map.put("D", "d");map.put("E", "e");Map<String, String> map2=new HashMap<>();map2.put("F", "f");map2.put("G", "g");map2.putAll(map);Set<String> keys=map2.keySet();for(String key:keys){String v1=map2.get(key);System.out.println(key+"------"+v1);}}
}
//结果
A------a
B------b
C------c
D------d
E------e
F------f
G------g
4.2.4 删除元素
package MyJava;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapTest {public static void main(String[] args) {//实例化HashMap容器Map<String, String> map=new HashMap<>();//添加元素map.put("A", "a");map.put("B", "b");map.put("C", "c");map.put("D", "d");map.put("E", "e");Map<String, String> map2=new HashMap<>();map2.put("F", "f");map2.put("G", "g");map2.putAll(map);map2.remove("D");map2.remove("G");Set<String> keys=map2.keySet();for(String key:keys){String v1=map2.get(key);System.out.println(key+"------"+v1);}}
}
//结果
A------a
B------b
C------c
E------e
F------f
4.2.5 判断key或value是否存在
package MyJava;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapTest {public static void main(String[] args) {//实例化HashMap容器Map<String, String> map=new HashMap<>();//添加元素map.put("A", "a");map.put("B", "b");map.put("C", "c");map.put("D", "d");map.put("E", "e");Map<String, String> map2=new HashMap<>();map2.put("F", "f");map2.put("G", "g");map2.putAll(map);map2.remove("D");map2.remove("G");Set<String> keys=map2.keySet();for(String key:keys){String v1=map2.get(key);System.out.println(key+"------"+v1);}System.out.println(map2.containsKey("A"));System.out.println(map2.containsValue("f"));}
}
//结果
A------a
B------b
C------c
E------e
F------f
true
true
4.2.6 HashMap底层源码分析
4.2.6.1 底层存储介绍
HashMap底层实现采用了哈希表,这是一种非常重要的数据结构,对于以后我们以后理解很多技术都非常有帮助。因此,大家需要有必要了解一下。
数据结构中采用数组和链表来实现对数据的存储,它们各有特点:
-
数组:占用空间连续,寻址容易,查询速度快,但是增加和删除效率非常低。
-
链表:占用空间不连续,寻址困难,查询速度慢,但是增加和删除效率都非常高。
那么我们可不可以结合数组和链表的特点(查询快,增加和删除效率高),答案就是:哈希表。哈希表的本质就是数组+链表。
Ctrl+鼠标的点击
4.3 TreeMap容器类
TreeMap和HashMap同样实现了Map接口,所以,对于API的用法来说是没有区别的,HashMap效率高于TreeMap,TreeMap是可以对键进行排序的一种容器,在需要对键进行排序时可选用TreeMap,TreeMap底层是基于红黑树实现的。
在使用TreeMap时需要给定排序规则
-
通过自身来实现比较规则
package MyJava; public class Users implements Comparable<Users>{private String username;private int userage;public Users(String username,int userage) {this.userage=userage;this.username=username;}public Users() {// TODO Auto-generated constructor stub}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public int getUserage() {return userage;}public void setUserage(int userage) {this.userage = userage;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn "Users{"+"username:"+username+"userage:"+userage+"}"; }@Overridepublic boolean equals(Object obj) {System.out.println("equals...");if(this==obj) return true;if(obj==null||getClass() !=obj.getClass()) return false;Users users=(Users)obj;if(userage !=users.userage) return false;return username !=null?username.equals(users.username):null;}@Overridepublic int hashCode() {int result=username !=null ?username.hashCode():0;result=31*result+userage; return result;}@Overridepublic int compareTo(Users o) {// TODO Auto-generated method stubif(this.userage>o.getUserage()){return 1;}if(this.userage==o.getUserage()){return this.username.compareTo(o.getUsername());}return -1;} } package MyJava; import java.util.Map; import java.util.Set; import java.util.TreeMap; public class TreeMapTest {public static void main(String[] args) {//实例化TreeMapMap<Users, String> map=new TreeMap<>();Users u1=new Users("xiaoxie",10);Users u2=new Users("xiaozhang",11);map.put(u1, "nihao");map.put(u2, "nihaoya");Set<Users> keys=map.keySet();for(Users key:keys){System.out.println(key+"-----"+map.get(key));}} } //结果 Users{username:xiaoxieuserage:10}-----nihao Users{username:xiaozhanguserage:11}-----nihaoya
-
通过比较器来实现比较规则
package MyJava;
public class Student {private String name;private int age;public Student(String name,int age) {// TODO Auto-generated constructor stubthis.name=name;this.age=age;}public Student() {// TODO Auto-generated constructor stub}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;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn "Name:"+name+""+"Age:"+age;}
}
//比较器
package MyJava;
import java.util.Comparator;
public class StudentCompara implements Comparator<Student>{//定义比较规则 @Overridepublic int compare(Student o1, Student o2) {// TODO Auto-generated method stubif(o1.getAge()>o2.getAge()){return 1;}if(o1.getAge()==o2.getAge()){return o1.getName().compareTo(o2.getName());}return -1;}
}
//测试
package MyJava;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapTest {public static void main(String[] args) {//实例化TreeMapMap<Student, String> map=new TreeMap<>(new StudentCompara());Student u1=new Student("xiaoxie",10);Student u2=new Student("xiaozhang",11);Student u3=new Student("xiaoming",11);map.put(u1, "nihao");map.put(u2, "nihaoya");map.put(u3, "nizhencai");Set<Student> keys=map.keySet();for(Student key:keys){System.out.println(key+"-----"+map.get(key));}}
}
5.Iterator 迭代器
5.1 Iterator迭代器接口介绍
Collection接口继承了Iterable接口,在该接口中包含了一个名为iterator的方法,所有实现了Collection接口的容器类都对该方法做了具体的实现,iterator会返回一个Iterator接口类型的迭代器对象,在该对象中包含了三个方法用于实现对单例容器的迭代处理。
Iterator接口定义了如下方法:
-
boolean hasNext()://判断游标当前位置是否有元素,如果有返回true,没有返回false;
-
object next();//获取当前游标所在位置的元素,并将游标移动到下一个位置;
-
void remove();删除游标当前位置的元素,在执行next以后,该操作只能执行一次。
5.2 迭代器的使用
5.2.1 使用Iterator迭代List接口类型容器
package MyJava;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public class iteratorTest {public static void main(String[] args) {//实例化ArrayListList<String> list=new ArrayList<>();//添加元素list.add("a");list.add("b");list.add("c");list.add("d");list.add("e");//获取元素//获取迭代器对象Iterator<String> iterator=list.iterator();//方式一:在迭代器中,通过while循环来获取元素while(iterator.hasNext()){String value=iterator.next();System.out.println(value);}//方式二:在迭代器中,通过for循环来获取元素,自己写的for(int i=0;i<list.size();i++){if(iterator.hasNext()){String value1=iterator.next();System.out.println(value1);}else{break;}}//方式二:通过for循环来获取元素,老师写的for(Iterator<String> it=list.iterator();it.hasNext();){//先获取一个对象,然后对对象进行判断String value2=it.next();System.out.println(value2);}}
}
5.2.2 使用Iterator迭代Set接口类型容器
package MyJava;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class iteratorTest {public static void main(String[] args) {//实例化Set类型的容器Set<String> set=new HashSet<>();//添加元素set.add("a");set.add("b");set.add("c");set.add("d");set.add("e");//获取元素//方式一:使用while循环来获取元素Iterator<String> iterator=set.iterator();while(iterator.hasNext()){String value=iterator.next();System.out.println(value);}//方式二:通过for循环来获取元素for(Iterator<String> iterator2 = set.iterator();iterator2.hasNext();){String value2=iterator2.next();System.out.println(value2);}}
}
5.2.3 在迭代器中删除元素
package MyJava;
import java.util.ArrayList;
import java.util.List;
public class iteratorTest {public static void main(String[] args) {//实例化Set类型的容器List<String> list=new ArrayList<>();//添加元素list.add("a");list.add("b");list.add("c");list.add("d");list.add("e");//删除元素int flag=-1;for(int i=0;i<list.size();i++){//list.remove(2);//错误的//list.add("ddd");if("c".equals(list.get(i))){flag=i;if (flag>-1) {list.remove(flag);}}}for(String str1:list){System.out.println(str1);}}
}
6.Collections 工具类
Collections是一个工具类,它提供了对Set、List、Map进行·排序、填充、查找元素的辅助方法。该类中所有的方法都是静态方法。
常用方法:
方法 | 说明 |
---|---|
void sort(List) | 对List容器内的元素进行排序,排序的规则是按升序排序 |
void shuffle(List) | 对List内的元素进行随机排列 |
void reverse(list) | 对List内的元素进行逆序排列 |
void fill(List,object) | 用一个特定的对象重写整个List容器 |
int binarySearch(List,object) | 对于顺序的List容器,采用折半查找的方法查找特定对象 |
sort ()升序排序
package MyJava;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionTest {public static void main(String[] args) {List<String> list=new ArrayList<>();list.add("a");list.add("b");list.add("f");list.add("g");list.add("e");//通过Cllections工具类中的sort()方法来完成排序Collections.sort(list);for(String str1:list){System.out.println(list);}}
}
shuffle()随机排序
package MyJava;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionTest {public static void main(String[] args) {List<String> list=new ArrayList<>();list.add("a");list.add("b");list.add("f");list.add("g");list.add("e");//通过Cllections工具类中的sort()方法来完成排序Collections.shuffle(list);for(String str1:list){System.out.println(list);}}
}
数据结构
主要内容
-
数据结构简介
-
线性结构
-
树形结构
学习目标
知识点 | 要求 |
---|---|
数据结构简介 | 了解 |
线性结构 | 了解 |
树形结构 | 了解 |
数据结构的简介
1.什么是数据结构
简单地说,数据结构就是以某种特定的布局方式存储数据的容器,这种布局方式决定了数据结构对于某些操作时高效的,而对于其它操作是低效的,所以我们需要理解各种数据结构,才能在处理实际问题时,选取最合适的数据结构。
数据结构=逻辑结构+物理结构(顺序、链式、索引、散列)
逻辑结构:数据元素间相互的抽象化的关系。
物理结构:(存储结构),计算机存储器中的存储形式。
2.数据结构逻辑分类
数据结构从逻辑上划分为三种基本类型:
2.1线性结构
数据结构中的元素存在一对一的关系;
常见的线性结构:
线性表、栈、队列、串(一维数组)等。
2.2 树形结构
数据中的元素存在一对多的相互关系;
常见的树形结构:
二叉树、红黑树、B树、哈夫曼树等。
2.3图形结构
数据结构中的元素存在多对多的关系;
常见的图形结构:
有向图、无向图、简单图等。
线性结构
1.栈结构
1.1栈的定义
栈是一种只能从一端存取数据且遵循“后进先出”原则的线性存储结构;
1.2实现栈容器
1.2.1 创建栈容器类
package databaseStruct;
/*** 自定义栈容器* @param <E>* * */
public class MyStack<E> {private Object[] arr;//存取元素的物理结构private int stackLength=4;//数组的默认长度private int size;//记录栈容器的元素个数private int index=-1;//操作数组下标位置的指针//判断栈容器是否为空public boolean empty(){return false;} //获取栈顶元素public E pop(){return null;}//向栈容器中添加元素public E push(E item){return null;}public static void main(String[] args) {}
}
1.2.2 实现添加元素
//向栈容器中添加元素public E push(E item){//初始化数组this.capacity();//向数组中添加元素this.arr[++index]=item;//记录元素个数this.size++;return item;}//完成对数组初始化和以1.5的容量对数组增加扩容private void capacity(){//对数组初始化if(this.arr==null){this.arr=new Object[this.stackLength];}//以1.5倍对数组增加扩容if(this.size-(this.stackLength-1)>=0){this.stackLength=this.stackLength+(this.stackLength >>1);//右移一位就是除以2this.arr=Arrays.copyOf(this.arr, this.stackLength);}}
1.2.3 实现获取元素
//获取栈顶元素public E pop(){//如果栈容器中没有元素,则抛出异常if(this.index==-1){throw new EmptyStackException();}//记录元素个数this.size--;//如果有元素,返回栈顶元素return (E)this.arr[index--];//index--是先运算后加减嘛}
1.2.4 判断栈容器是否为空
//判断栈容器是否为空public boolean empty(){return this.size==0;}
2.链表结构
2.1 链表结构的介绍
2.1.1 什么是链表
链表结构是由许多节点构成的,每个节点都包含两个部分。
-
数据部分:保存该结点的实际数据
-
地址部分:保存上一个和下一个节点的地址
2.1.2 链表分类
-
单向链表
-
双向链表
-
双向循环链表
2.1.3 链表的特点
-
节点在存储器中的位置是任意的,即逻辑上相邻的两个数据元素在物理上不一定相邻
-
访问时只能通过头或尾针进入链表,并通过每个节点的指针域向后或向前扫描其余节点,所以寻找第一个节点和最后一个节点花费的时间不等。
链表的优缺点:
-
优点:数据元素的个数可以自由扩充、插入、删除等操作不必移动数据,只需修改链接指针,修改效率较高。
-
缺点:必须采用顺序存取,即存储数据元素时只能按链表的顺序进行访问,访问节点效率较低。
2.2 单向链表结构
2.2.1 单向链表的定义
单向链表是链表的一种,其特点是链表的连接方向是单向的,对链表的访问要通过从头部开始顺序读取。
2.2.2 实现单向链表
2.2.2.1 创建链表接口
package databaseStruct; public interface MyList<E> {void add(E element);E get(int index);int size();E remove(int index); }
2.2.2.2 创建单向链表类
package databaseStruct;
public class MySinglyLinkedList<E> implements MyList<E>{
@Overridepublic void add(E element) {//向链表中添加元素// TODO Auto-generated method stub}
@Overridepublic E get(int index) {//根据位置获取元素// TODO Auto-generated method stubreturn null;}
@Overridepublic int size() {//计算链表中的元素个数// TODO Auto-generated method stubreturn 0;}
@Overridepublic E remove(int index) {//根据位置删除元素// TODO Auto-generated method stubreturn null;}}
2.2.2.3 创建节点类
public class MySinglyLinkedList<E> implements MyList<E>{
//定义单向链表中的节点对象
///class Node<E>{private E item;//存储元素private Node next;//存储下一个节点对象的地址public Node(E item,Node next) {this.item=item;this.next=next;}}//
2.2.2.4 实现添加元素方法
private Node head;//存放列表中的头节点private int size;//记录元素个数@Overridepublic void add(E element) {//向链表中添加元素//创建节点Node<E> node=new Node<>(element, null);//找到尾节点Node tail=getTail();//节点的挂接if(tail==null){this.head=node;}else{tail.next=node;}//记录元素的个数this.size++;}private Node getTail() {//判断头节点是否存在if(this.head==null){return null;}//查找尾节点Node node=this.head;while(true){if(node.next==null)break;node=node.next;//移动指针,指向下一个节点}return node;}
2.2.2.5 实现获取元素的方法
@Overridepublic E get(int index) {//根据位置获取元素//校验index的合法性this.checkIndex(index);//根据位置获取指定节点NodNode<E> node=this.getNode(index);//将该节点中的元素返回return node.item;}//校验index的合法性private void checkIndex(int index){if(!(index>=0&&index<this.size)){throw new IndexOutOfBoundsException("index:"+index+"size:"+size);}}//根据位置获取指定节点private Node getNode(int index){Node<E> node=this.head;for(int i=0;i<index;i++){node=node.next;}return node;}
2.2.2.6 实现删除元素
public E remove(int index) {//根据位置删除元素// TODO Auto-generated method stub//对位置做一个校验this.checkIndex(index);//根据位置找到节点对象Node<E> node=this.getNode(index);//获取节点对象中的元素E item =node.item;//将该节点对象从单向链表中移除//判断当前删除的节点是否是头节点if(this.head==node){this.head=node.next;}else{Node<E> temp=this.head;for(int i=0;i<index-1;i++){temp=temp.next;}temp.next=node.next;}node.next=null;//记录元素个数this.size--;//将该元素返回return item;}
2.2.2.7 实现获取元素个数
@Override public int size() {//计算链表中的元素个数// TODO Auto-generated method stubreturn this.size;}
2.2.2.8 具体实现
public static void main(String[] args) {MySinglyLinkedList<String> mySinglyLinkedList=new MySinglyLinkedList<>();mySinglyLinkedList.add("a");mySinglyLinkedList.add("b");mySinglyLinkedList.add("c");mySinglyLinkedList.add("d");System.out.println(mySinglyLinkedList.size());System.out.println(mySinglyLinkedList.remove(0));System.out.println(mySinglyLinkedList.remove(2));System.out.println(mySinglyLinkedList.get(1));}
结果:4
a
d
c
2.3 双向链表结构
双向链表也叫双链表,是链表的一种,它的每个数据节点都有两个指针,分别指向直接前驱和直接后继。
2.3.1 创建双向链表类
package databaseStruct;
public class MyDoublyLinkedList<E> implements MyList<E>{
@Overridepublic void add(E element) {// TODO Auto-generated method stub}
@Overridepublic E get(int index) {// TODO Auto-generated method stubreturn null;}
@Overridepublic int size() {// TODO Auto-generated method stubreturn 0;}
@Overridepublic E remove(int index) {// TODO Auto-generated method stubreturn null;}}
2.3.2 创建节点类
//定义双向链表的节点对象class Node<E> {E item;//记录元素Node<E> prev;//记录前一个节点对象Node<E> next;//记录后一个节点对象public Node(Node<E> prev,E item,Node<E> next) {// TODO Auto-generated constructor stubthis.prev = prev;this.item = item;this.next = next;}}
2.3.3 实现添加元素方法
private Node head;//记录头节点private Node tail;//记录尾节点private int size;//记录元素个数@Overridepublic void add(E element) {this.LinkLast(element);}//将节点对象添加到双向链表的尾部private void LinkLast(E element){Node t =this.tail;//获取尾节点Node<E> node=new Node<>(t,element,null);//创建节点对象this.tail=node;//将新节点定义为尾节点if(t==null){this.head=node;}else{t.next=node;}this.size++;}
2.3.4 实现获取元素的方法
@Overridepublic E get(int index) {//对Index做合法性校验this.checkIndex(index);//根据位置查找节点对象Node<E> node = this.getNode(index);return node.item;}//校验index的合法性private void checkIndex(int index){if(!(index>=0&&index<this.size)){throw new IndexOutOfBoundsException("Index:"+index+"Size:"+size);}}//根据位置获取节点对象private Node getNode(int index){if(index<(this.size)/2){Node node = this.head;for(int i=0;i<index;i++){node=node.next; }return node;}else{Node node = this.tail;for(int i=this.size-1;i>index;i--){node = node.prev;}return node;}}
2.3.5 实现删除元素的方法
@Overridepublic E remove(int index) {//对Index作合法性校验this.checkIndex(index);//根据指定位置获取节点对象Node<E> node = this.getNode(index);//获取节点对象中的元素E item=node.item;//判断当前节点是否为头节点if(node.prev==null){this.head=node.next;}else{//完成当前节点的直接前驱和当前节点的直接后继节点的挂接node.prev.next=node.next;}//判断当前节点是否为尾节点if(node.next==null){this.tail=node.prev;}else{//完成当前节点的直接后继节点和当前节点的直接前驱节点的挂接node.next.prev=node.prev;}//断掉当前节点的链接node.prev=null;node.next=null;node.item=null;this.size--;return item;}
2.3.6 获取元素的个数
@Overridepublic int size() {// TODO Auto-generated method stubreturn this.size;}
2.3.7 实现在双向链表的头部添加元素
//将节点添加到双向链表的头部public void addFirst(E element){this.LinkFirst(element);}private void LinkFirst(E element){//获取头节点对象Node head =this.head;Node node = new Node<E>(null, element, head);//将新节点定义为头节点this.head=node;//判断当前链表中是否有节点,如果没有则该节点既是头节点也是尾节点if(head==null){this.tail=node;}else{head.prev=node;}this.size++;}
2.3.8 实现在双向链表的尾部添加元素
//将节点添加到双向链表的尾部public void addLast(E element){this.LinkLast(element);}//将节点对象添加到双向链表的尾部private void LinkLast(E element){Node t =this.tail;//获取尾节点Node<E> node=new Node<>(t,element,null);//创建节点对象this.tail=node;//将新节点定义为尾节点if(t==null){this.head=node;}else{t.next=node;}this.size++;}
树形结构
1.树形结构的简介:
树结构是一种非线性存储结构,存储的是具有”一对多“关系的数据元素的集合。
2.树的相关术语
-
结点(Node)
使用树结构存储的每一个数据元素都是“结点”。
-
结点的度(Degree of Node)
某个结点所拥有的子树的个数。
-
树的深度(Degree of Tree)
树中结点的最大层次数。
-
叶子结点(Leaf Node)
度为零的结点,也叫终端结点。
-
分支结点(Branch Node)
度不为零的结点,也叫非终端结点或内部结点。
-
孩子(child)
也可称为子树或者子结点,表示当前结点下层的直接结点。
-
双亲(Parent)
也可称之为父结点,表示当前结点的直接上层结点。
-
根结点(Root Node)
没有双亲结点的结点,在一个树结构中只有一个根结点。
-
祖先(Ancestor)
从当前结点上层的所有结点。
-
子孙(Descendant)
从当前结点下层的所有结点。
-
兄弟(Brother)
同一双亲的孩子。
3.二叉树的简介
二叉树是树形结构的一个重要类型,许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也可以转换为二叉树,而且二叉树的的存储结构及其算法都较为简单,因此二叉树显得特别重要,二叉树的特点是每个结点最多有两课子树。且有左右之分。
4. 二叉树的分类
4.1 满二叉树
满二叉树是指除了最后一层外,每一层上的结点都有两个子结点。
4.2 完全二叉树
完全二叉树,除了最后一层可能不满以外,其他各层都达到该层节点的最大数,最后一层如果不满,该层所有节点都全部靠左排。
5.二叉树的遍历方式
二叉树的遍历方式:
-
前序遍历:根-左-右
-
中序遍历:左-根-右
-
后序遍历:左-右-根
-
层序遍历:从上至下逐层遍历
6.二叉树排序分析
利用二叉树结构以及遍历方式可以实现基于二叉树的元素排序处理(如下图:使用中序遍历即可排好序了)
7. 创建二叉树排序器类
package databaseStruct;
public class BinaryTreeSort<E extends Integer> {//将元素添加到排序器中public void add(E element){}//对元素进行排序public void sort(){}public static void main(String[] args) {}
}
8.创建节点类
//定义节点类class Node<E extends Integer>{private E item;//存放元素private Node left;//存放左子树地址private Node right;//存放右子树地址public Node(E item) {//left和right让他为空就可以了this.item=item;}//添加节点public void addNode(Node node){//完成新节点的元素和当前的结点的元素的判断//如果小于,那么新节点放在当前节点的左子树中if(node.item.intValue()<this.item.intValue()){if(this.left==null){this.left=node;}else{this.left.addNode(node);}}//如果大于,那么新节点放在当前节点的左子树中else{if(node.item.intValue()>this.item.intValue()){if(this.right==null){this.right=node;}else{this.right.addNode(node);}}}}//使用中序遍历二叉树public void inorderTraversal(){//找到最左侧的那个节点,if(this.left !=null){this.left.inorderTraversal();}System.out.println(this.item);//在压栈的时候,不执行该语句的,找到目标了,就会弹栈,弹栈时是先执行该语句的if(this.right !=null){this.right.inorderTraversal();}}}
注意:
递归调用有一个压栈和弹栈的操作,在压栈的时候,System.out.println(this.item);是不执行的,找到目标了,就会弹栈,弹栈时是先执行该语句的。如下,首先我们输出4,然后在4这个节点找该节点的右节点,没有,然后弹回6这个结点,输出6,再找6这个节点的右节点,有,输出7,找7这个节点的左右结点都没有,弹回8这个结点,找8这个节点的右节点,有,输出9,找9这个结点的左右结点,都为空,遍历完毕。
9.实现在排序器中添加元素方法
private Node root;//存放根节点的地址//将元素添加到排序器中public void add(E element){//实例化节点对象Node<E> node=new Node<>(element);//判断当前二叉树是否有根节点,如果没有,那么新节点就为根节点if(this.root==null){this.root=node;}else{this.root.addNode(node);}}
10.实现排序器中的排序方法
//对元素进行排序public void sort(){//判断根节点是否为空if(this.root==null){return;}this.root.inorderTraversal();}
11.调试
public static void main(String[] args) {BinaryTreeSort<Integer> sort=new BinaryTreeSort<>();//12,10,5,7,3,1sort.add(12);sort.add(1);sort.add(7);sort.add(3);sort.add(10);sort.add(5);sort.sort();}
//调试结果
1
3
5
7
10
12
自定义树形结构容器
1.树形结构定义(五个条件)
-
能够找到当前节点的父节点
-
能够找到当前节点的子节点
-
能够找到当前节点的兄弟结点
-
能够找到当前节点的祖先节点
-
能够找到当前节点的子孙节点
2.自定义树形结构分析
3.实现自定义树形结构容器
3.1 创建树形结构容器类
package databaseStruct;
import java.util.List;
public class MyTree <E>{//向容器中添加元素public void add(E parent,E item){}//获取当前节点的父节点public E getParent(E item){return null;}//获取当前节点的子节点public List<E> getChild(E item){return null;}//获取当前节点的兄弟节点public List<E> getBrother(E item){return null;}//获取当前节点的祖先节点public List<E> getForeFathers(E item){return null;}//获取当前节点的子孙节点public List<E> getGrandChildren(E item){return null;}
}
3.2 实现添加元素方法
利用Map容器的映射关系和List来实现添加元素的方法
private Map<E, E> map=new HashMap<>();//String———>String private Map<E, List<E>> map2=new HashMap<>();//String———>List//向容器中添加元素public void add(E parent,E item){//先完成树中的单节点之间的映射this.map.put(item, parent);//完成多节点之间的映射List<E> list=this.map2.get(parent);//判断当前节点是否有子节点,如果没有则创建一个新的Listif(list==null){list=new ArrayList<>();this.map2.put(parent, list);}list.add(item);}
3.3 获取当前节点的父节点和子节点
-
获取父节点
//获取当前节点的父节点public E getParent(E item){return this.map.get(item);}
-
获取子节点
//获取当前节点的子节点public List<E> getChild(E item){return this.map2.get(item);}
3.4 获取当前节点的兄弟节点
public List<E> getBrother(E item){//获取当前节点的父节点E parent=this.getParent(item);//获取当前父节点的所有子节点List<E> list=this.getChild(parent);List<E> brother=new ArrayList<>();if(list !=null){brother.addAll(list);brother.remove(item); }return brother;}
3.5 获取当前节点的祖先节点
//获取当前节点的祖先节点public List<E> getForeFathers(E item){//获取当前节点的父节点E parent =this.getParent(item);//结束递归的边界条件if(parent==null){return new ArrayList<>();}//递归调用,再次获取当前节点的父节点的父节点List<E> list=this.getForeFathers(parent);//将递归到的所有结点元素添加到返回的List当中list.add(parent);return list;}
3.6 获取当前节点的子孙节点
//获取当前节点的子孙节点public List<E> getGrandChildren(E item){//存放所有子孙节点中的元素List<E> list=new ArrayList<>();//获取当前节点的子节点List<E> child=this.getChild(item);if(child==null){return list;}//遍历子节点for(int i=0;i<child.size();i++){//获取节点中的元素E ele=child.get(i);List<E> temp=this.getGrandChildren(ele);list.add(ele);list.addAll(temp);}return list;}
3.7 测试自定义容器
public static void main(String[] args) {//实例化容器MyTree<String> myTree=new MyTree<>();//向容器里添加元素myTree.add("root", "生物");myTree.add("生物", "植物");myTree.add("生物", "动物");myTree.add("生物", "菌类");myTree.add("动物", "脊索动物");myTree.add("动物", "脊椎动物");myTree.add("动物", "腔肠动物");myTree.add("脊椎动物", "哺乳动物");myTree.add("脊椎动物", "鱼类");myTree.add("哺乳动物", "猫");myTree.add("哺乳动物", "牛");myTree.add("哺乳动物", "人");//获取父节点System.out.println("----------获取父节点");String parent=myTree.getParent("鱼类");System.out.println(parent);System.out.println("----------获取子节点");List<String> child=myTree.getChild("动物");for(int i=0;i<child.size();i++){System.out.println(child.get(i));}//获取兄弟结点System.out.println("-----------获取兄弟节点");List<String> brother = myTree.getBrother("脊椎动物");for(int j=0;j<brother.size();j++){System.out.println(brother.get(j));}//获取祖先结点System.out.println("----------获取祖先节点");List<String> foreFather=myTree.getForeFathers("人");for(int i=0;i<foreFather.size();i++){System.out.println(foreFather.get(i));}//获取子孙结点System.out.println("----------获取子孙节点");List<String> grandChildren=myTree.getGrandChildren("root");for(int i=0;i<grandChildren.size();i++){System.out.println(grandChildren.get(i));}}
测试结果:----------获取父节点
脊椎动物
----------获取子节点
脊索动物
脊椎动物
腔肠动物
-----------获取兄弟节点
脊索动物
脊椎动物
腔肠动物
----------获取祖先节点
root
生物
动物
脊椎动物
哺乳动物
----------获取子孙节点
生物
植物
动物
脊索动物
脊椎动物
哺乳动物
猫
牛
人
鱼类
腔肠动物
菌类
Io流
主要内容
-
Io简介
-
Io流入门案例
-
File类的使用
-
常用流对象
-
Apache IO包
-
本章总结
学习目标
知识点 | 要求 |
---|---|
Io简介 | 了解 |
Io入门案例 | 了解 |
File类的使用 | 掌握 |
常用流对象 | 掌握 |
Apache IO包 | 掌握 |
本章总结 | 掌握 |
IO简介
1.什么是IO
对于任何程序设计语言而言,输入输出系统都是非常核心的功能,程序运行需要数据,数据的获取往往需要跟外部系统进行通信,外部系统可能是文件、数据库、其他程序、网络、Io设备等等。那么我们有必要通过某种手段进行抽象、屏蔽外部的差异,从而实现更加便捷的编程。
输入(input)指的是:可以让程序从外部系统获得数据,(核心含义是“读”,读取外部数据)。
常见的应用:
-
读取硬盘上的文件内容到程序,例如:播放器打开一个视频文件,word打开一个doc文件。
-
读取网络上某个位置内容到程序,例如:浏览器输入某个网址后,打开该网址对应的网页内容,下载网络上某个网址的文件
-
读取数据库的数据到程序
-
读取某个硬件系统数据到程序,例如:车载电脑读取雷达扫描信息到程序,温控系统等。
输出(output)指的是:程序输出数据到外部系统从而操作外部系统,(核心含义是“写”,将数据写到外部系统)。
常见的应用:
-
将数据写到硬盘中,例如:我们编辑完一个word文档后,将内容写道硬盘上进行保存、
-
将数据写到数据库中,例如:我们注册一个网站会员,实际上就是后台程序向数据库中写入一条记录
-
将数据写到某些硬盘系统中,例如:导弹系统导航程序将新的路径输入到飞控子系统,飞控子系统根据数据修正飞行路径
java.io包为我们提供了相关的API实现了对外部系统的输入输出操作,这就是我们这章所要学习的技术。
2. 数据源
数据源,提供数据的原始媒介,常见的数据源有:数据库、文件、其他程序、内存、网络连接、Io设备等。
数据源分为原设备、目标设备。
-
原设备:为程序提供数据,一般对应输入流。
-
目标设备:程序数据的目的地,一般对应输出流。
3.流的概念
流是一个抽象、动态的概念,是一连串连续的动态的数据集合。
对于输入流而言,数据源就像水箱,流就像水管里面流出来的水流,程序就是我们最终的用户,我们通过流将数据源中的数据输送到程序中。
对于输出流而言,目标数据源就是目的地,我们通过流将程序中的数据输送到目的数据源中。(输入/输出流是相对于程序而言的,并不是相对于数据源)
4.java中的四大IO抽象类
InputStream/OutputStream和Reader/Writer类是所有IO流类的抽象父类。我们有必要了解一下这四个抽象类的作用,然后通过它们具体的子类熟悉相关的用法。
(1)InputStream
此抽象类是表示字节输入流的所有类的父类,InputStream是一个抽象类,他不可以实例化,数据的读取需要它的子类来实现,根据节点的不同,他派生了不同的节点流子类。
继承InputStream的流都是向程序中输入数据,且数据的单位都是8bit.
常用的方法:
-
int read():读取一个字节的数据,并将字节的值作为int型返回(0-255之间的值)。如果未读出字节,则返回-1,读取结束。
-
void close():关闭输入流对象,释放相关数据资源。
(2)OutputStream
此抽象类是表示所有输出流的所有类的父类,输出流接受输出字节并将这些字节发送到某个目的地。
常用方法:
-
void write(int n):向目的地中写入一个字节
-
void close():关闭输出流对象,释放相关系统资源。
(3)Reader
Reader用于读取字符流抽象类,数据单位为字符。
常用方法:
-
int read():读取一个字符的数据,并将字符的值作为int类型(0-65535之间的一个值,即Unicode值)。如果未读出字符则返回-1,读取结束。
-
void close():关闭流对象,释放相关系统资源。
(4)Writer
Writer用于输出的字符流抽象,数据单位为字符。
常用方法:
-
void write(int n):向输出流中写入一个字符
-
void close():关闭输出流对象,释放相关系统资源。
5.Java中流的概念细分
按流的方向分类
-
输入流:数据流从数据源到程序(以InputStream、Reader结尾的流)
-
输出流:数据流从程序到目的地(以OutputStream、Writer结尾的流)
按处理的数据单元分类
-
字节流:以字节为单位获取数据,命名上以Strean为结尾的流一般都是字节流,如:FileInputStream、FileOutputStream。
//1、输入流 package IOStream; import java.io.FileInputStream; import java.io.IOException; public class Demo_File {public static void main(String[] args) throws IOException {FileInputStream fileInputStream = new FileInputStream("src/xxx.txt");int x;while((x =fileInputStream.read()) != -1){System.out.println(x);}fileInputStream.close();} } //假如xxx.txt文件里面有abc //输出 97 98 99 package IOStream; import java.io.*; public class Demo_File {public static void main(String[] args) throws IOException {FileInputStream fileInputStream = new FileInputStream("src/xxx.txt");int b;while ((b = fileInputStream.read()) != -1){char c = (char)b;System.out.print(c);}fileInputStream.close();} } //输出 abcdefghijk//2、输出流package IOStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Demo_File {public static void main(String[] args) throws IOException {FileOutputStream fileOutputStream = new FileOutputStream("src/sss.txt",true);//输出流可以帮我们创建没有的文件sss.txt//加true表示在原来的基础上追加,不加true添加内容的话会覆盖原来的内容fileOutputStream.write(97);fileOutputStream.write(98);fileOutputStream.write(90);fileOutputStream.close(); } } //在创建的sss.txt中添加了内容 abZ//3、对文件的拷贝//普通拷贝public class Demo_File {public static void main(String[] args) throws IOException {FileInputStream fileInputStream = new FileInputStream("src/Images/刘德华.png");FileOutputStream fileOutputStream = new FileOutputStream("src/Images/刘德华2.png");int b ;while ((b=fileInputStream.read()) != -1){fileOutputStream.write(b);}fileInputStream.close();fileOutputStream.close();}//available()方法拷贝public static void main(String[] args) throws IOException {FileInputStream fileInputStream = new FileInputStream("src/Images/刘德华.png");FileOutputStream fileOutputStream = new FileOutputStream("src/Images/刘德华3.png");byte[] bytes = new byte[fileInputStream.available()];//太暴力//available()返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。fileInputStream.read(bytes);fileOutputStream.write(bytes);fileInputStream.close();fileOutputStream.close();//定义小数组拷贝public static void main(String[] args) throws IOException {FileInputStream fileInputStream = new FileInputStream("src/Images/刘德华.png");FileOutputStream fileOutputStream = new FileOutputStream("src/Images/刘德华4.png");byte[] bytes = new byte[1024*8];int len;while((len = fileInputStream.read(bytes)) != -1){fileOutputStream.write(bytes,0,len);//bytes写多少,0从哪开始,len有效字节}fileInputStream.close();fileOutputStream.close();}
字节流总结:读取的是二进制,要读取字符需将二进制char强转,在读取汉字时会出现乱码;写出时不可汉字、字符,只能写出二进制,如果要写出汉字字符时,字节流直接操作字节,所以写出的中文把必须字符串转换为数组 write("文字".getBytes())
package IOStream; import java.io.*; import java.nio.charset.StandardCharsets; public class Demo_File {public static void main(String[] args) throws IOException {FileOutputStream fileOutputStream = new FileOutputStream("src/xxx.txt");fileOutputStream.write("尿频".getBytes(StandardCharsets.UTF_8)); } } //在xxx.txt文件中写入 尿频package IOStream; import java.io.*; import java.nio.charset.StandardCharsets; public class Demo_File {public static void main(String[] args) throws IOException {FileOutputStream fileOutputStream = new FileOutputStream("src/xxx.txt",true);//true追加fileOutputStream.write("hello!;''.'".getBytes(StandardCharsets.UTF_8)); } } //写入 尿频hello!;''.'
-
Flush 和 close方法的区别
-
Flush:用来刷新缓冲区,刷新完还会再次写出
-
close:关闭流,释放资源,不但会关闭资源,还会关闭刷新缓冲区
-
读写问题:读写中文时会出现乱码(有时候是半个中文)
-
写出问题:字节流直接操作字节,所以写出的中文把必须字符串转换为数组 write("文字".getBytes())
-
-
字符流:以字符为单位获取数据,命名上以Reader、Writer结尾的都是字符流,如:FileReader、FileWriter。
package IOStream; import java.io.*; public class Demo_File {public static void main(String[] args) throws IOException {FileReader fileReader = new FileReader("src/xxx.txt");int b ;while((b = fileReader.read()) != -1){char c = (char)b;System.out.print(c);}fileReader.close();} } //结果 你好,再见!package IOStream; import java.io.*; public class Demo_File {public static void main(String[] args) throws IOException {FileReader reader = new FileReader("src/xxx.txt");FileWriter writer = new FileWriter("src/yyy.txt");int ch;while((ch = reader.read()) != -1){writer.write(ch);}reader.close();writer.close();} }
-
readLine():可以读取一行字符(不包含换行符)
-
newLine():可以输出一个看跨平台的换行符
package IOStream; import java.io.*; public class Demo_File {public static void main(String[] args) throws IOException {BufferedReader reader = new BufferedReader(new FileReader("src/xxx.txt"));BufferedWriter writer = new BufferedWriter(new FileWriter("src/yyy.txt"));String line;while((line = reader.readLine()) != null){writer.write(line); // writer.write("\r\n");//只支持Windowswriter.newLine();//快平台的换行}reader.close();writer.close();} } //xxx.txt nihao你好! nihao你好! nihao你好! nihao你好! //yyy.txt nihao你好! nihao你好! nihao你好! nihao你好!
LineNumberReader:行号
-
getLineNumber():获取行号
-
setLineNumber():给行号
-
-
字符流总结:Reader读取的是二进制,要读取字符需将二进制char强转,读取汉字不会出现乱码,Write写入什么都给你转换为字符(输入字符或二进制都会转换为字符)
按处理对象不同分类
-
节点流:可以字节从数据源和目的地读取数据,如:FileInputStream、FileReader、DataInputStream等。
-
处理流:不直接连接数据源或目的地,是处理流的流,通过对其它流的处理提高程序的性能,如:BufferIednputStream、BufferedReader等,处理流也叫包装流。
节点流处于IO操作的第一线,所有操作必须通过他们进行,处理流可以对节点流进行包装,提高性能和提高程序的灵活性。
6. JAVA中IO流的体系
Java为我们提供了多种多样的IO流,我们可以通过不同的功能及性能要求挑选合适的IO流。
做个简单的总结:
-
InputStream/OutputStream
-
字节流的抽象类
-
-
Reader/Writer
-
字符流的抽象类
-
-
FileInputStream/FileOutputStream
-
字节流,以字节为单位直接操作”文件“。
-
-
ByteArrayInputStream/ByteOutputStream
-
字节流,以字节为单位直接操作“字节数组对象”,
-
-
ObjectInputStream/ObjectOutputStream
-
处理流,以字节为单位直接处理”对象“
-
-
DataInputStream/DataOutputSream
-
处理流,以字节为单位直接处理“基本数据类型和字符串类型”
-
-
FileReader/FileWriter
-
字符流,以字符为单位直接操作“文本文件”,(注意:只能读写文本文件)
-
-
BufferedReader/BufferedWriter
-
处理流,将Reader/Writer对象进行包装,增加缓存功能,提高读写效率
-
-
BufferedInputStream/BufferedOutputStream
-
处理流,将InputStream/OutputStream进行包装,增加缓存功能,提高读写效率
-
内置一个缓冲区(数组)
-
缓冲区思想
-
读取一个字节的时候,一次性从文件中读取8192个字节,下次读取就从缓冲区中拿就行了,直到用完,再读取下一个8192
-
//文件的拷贝,拷贝快
package IOStream;
import java.io.*;
public class Demo_File {public static void main(String[] args) throws IOException {FileInputStream fileInputStream = new FileInputStream("src/Images/刘德华.png");FileOutputStream fileOutputStream = new FileOutputStream("src/Images/刘德华5.png");//缓冲区BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);int b;while ((b = bufferedInputStream.read()) != -1){bufferedOutputStream.write(b);}bufferedInputStream.close();bufferedOutputStream.close();}
}
-
InputStreamReader/OutputStreamWriter
-
处理流,将字节流对象转化为字符流对象
-
-
PrintStream
-
处理流,将OutputStream进行包装,可以方便的输出字符,更加灵活。
-
上面的解释,一句话就点中了流的核心作用,要用心体会。
File类
1、File类的概述
-
File类更应该叫路径
-
文件路径和文件夹路径
-
路径分为绝对路径和相对路径
-
绝对路径:一个固定的路径,从盘符开始
-
相对路径:相对于某个路径,在eclipse下是指当前项目下
2、构造方法
/**\** File(File parent, String child)* 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。* File(String pathname)* 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。* File(String parent, String child)* 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。* File(URI uri)* 通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。*/
File file = new File("src/Images/刘德华.png");System.out.println(file.exists());//判断是否存在这个路径的文件String file01 = "src";String child = "xxx.txt";File file1 = new File(file01,child);System.out.println(file1.exists());
3、File类的创建功能
public static void main(String[] args) throws IOException {//public boolean creatNewFile();创建文件,如果存在就不会创建//public boolean mikdir();创建文件夹//public boolean mikdirs();创建文件夹,没有这个路径的话会创建这个路径File file = new File("yyy.txt");System.out.println(file.createNewFile());File file1 = new File("src");File file2 = new File(file1,"sss.txt");file2.createNewFile();File file1 = new File("zzz");System.out.println(file1.mkdir());File file2 = new File("sss\\ddd");//假如并没有sss这个文件夹System.out.println(file2.mkdirs());}
4、File类的重命名和删除功能
//重命名
public boolean renameTo(File dest);//重命名指定的路径,重命名之后还有一个剪切效果
//删除文件或文件夹
public boolean delete();
//要删除一个文件夹,文件夹里面不能有文件或文件夹
5、File类的判断功能
p
ublic boolean isDerectory();//判断是否是目录
public boolean isFile();//判断是否是文件
public boolean exists();//判断是否存在
public boolean canRead()//判断是否可读
public boolean canWrite();//判断是否可写
public boolean isHidden();//判断是否隐藏
6、File类的获取功能
public String getAbsolutePath();//获取绝对路径
public String getPAth();//获取相对路径
public String getName();//获取文件名
public long length();//获取长度、字节
public long lastModified();//获取最后一次修改时间(毫秒值)
public String[] list();//获取指定目录下所有文件或文件名称的数组
public File[] listFiles();//获取指定目录下所有的文件或文件夹的File数组
/**
public String[] list();//获取指定目录下所有文件或文件名称的数组
public File[] listFiles();//获取指定目录下所有的文件或文件夹的File数组
public static void main(String[] args) {File file = new File("sss");String[] str = file.list();for (String string:str) {System.out.println(string);}File[] file1 = file.listFiles();for (File f:file1) {System.out.println(f);}}}
//结果
ddd
sss\ddd
*/
String 里面有一个方法 endsWith() 来判断文件是否有这个后缀,并返回,一般在遍历文件时候使用
package IOStream;
import java.io.File;
import java.util.Scanner;
/*** 找出文件夹下的所有.java文件*/
public class Demo_File {public static void main(String[] args) {File dir = getDir();printJavaFile(dir);}public static File getDir() {Scanner scanner = new Scanner(System.in);System.out.println("请输入需要查找的文件夹名");while (true) {String str = scanner.next();File dir = new File(str);if (!dir.exists()) {System.out.println("请输入正确路径");} else if (dir.isFile()) {System.out.println("不能是一个文件");} else {return dir;}}}public static void printJavaFile(File dir){File[] files = dir.listFiles();for (File f:files) {if (f.isFile()&&f.getName().endsWith(".java")){System.out.println(f);}else if(f.isDirectory()){printJavaFile(f);}}}
}
//结果
sss
sss\a.java
sss\ddd\b.java
7、(整合文件)SequenceInputStream类
-
整合文件
//整合两个文件 package IOStream; import java.io.*; public class Demo_File {public static void main(String[] args) throws IOException {FileInputStream file = new FileInputStream("src/xxx.txt");FileInputStream file1 = new FileInputStream("src/yyy.txt");SequenceInputStream ses = new SequenceInputStream(file,file1);FileOutputStream fileOut = new FileOutputStream("zzz.txt");int b;while((b = ses.read()) != -1){fileOut.write(b);}ses.close();fileOut.close();} } //整合多个文件 package IOStream; import java.io.*; import java.util.Enumeration; import java.util.Vector; public class Demo_File {public static void main(String[] args) throws IOException {FileInputStream file = new FileInputStream("src/xxx.txt");FileInputStream file1 = new FileInputStream("src/yyy.txt");FileInputStream file2 = new FileInputStream("src/sss.txt");Vector<InputStream> v = new Vector<>();v.add(file);v.add(file1);v.add(file2);Enumeration<InputStream> e = v.elements();SequenceInputStream ses = new SequenceInputStream(e);FileOutputStream fout = new FileOutputStream("zzz.txt");int b;while ((b = ses.read()) != -1){fout.write(b);}ses.close();fout.close(); } }
8、文件名称过滤器的概述及其使用
public String[] list(FilenameFilter filter);
public File[] listFiles(FileFilter filter);
//假设在src目录下有sss.txt和xxx.txt两个文件
package IOStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
public class Demo_File {public static void main(String[] args) throws IOException {File file = new File("src");String[] strings = file.list(new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {//dir盘符File file1 = new File(dir,name);return file1.isFile() && file1.getName().endsWith(".txt");}});for(String str : strings){System.out.println(str);}}
}
//结果
sss.txt
xxx.txt