2 面向对象基础
三大特征:
一 封装:
概念:是指隐藏对象的属性和实现细节,仅对外提供访问的方式
好处:将变化隔离;便于使用;提高重用性;安全性。
单例设计模式:
解决的问题:保证一个类在内存中的对象唯一性。
Runtime()方法就是单例设计模式进行设计的。
如何保证对象的唯一性呢?
思想:
1,不让其它程序创建该对象。
2,在本类中创建一个本类对象。
3,对外提供方法,让其他程序获取这个对象。
//饿汉式
class Single
{
private Single(){}//私有化构造函数
private Static Single s = new Single();//创建私有并静态的本类对象。
public Static Single getInstance(){return s}//定义共有并静态的方法,返回该类对象
}
------------------------------
//懒汉式
class Single2
{
private Single2(){}
private static Single2 s=null;
public static Single2 getInstance()
{
if(s==null)
s = new Single2();
return s;
}
}
二 继承
好处:
1:提高了代码的复用性。
2:让类与类之间产生了关系。
final特点
1:这个关键字是一个修饰符,可以修饰类,方法,变量。
2:被final修饰的类是一个最终类,不可以被继承。
3:被final修饰的方法是一个最终方法,不可以被覆盖。
4:被final修饰的变量是一个常量,只能赋值一次。
抽象类的特点:
1:抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰。
2:抽象方法只定义方法的声明,并不定义方法的实现。
3:抽象类不可以被创建对象。
4:只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
5:抽象只能单继承。
接口:
抽象类和接口的区别:
1:抽象类只能被继承,而且只能单继承。
接口需要被实现,而且可以多实现。
2:抽象类中可以定于非抽象方法,子类可以直接继承使用。
接口中都有抽象方法,需要子类去实现。
3:抽象类使用的是 is a关系
接口使用的是like a关系
4:抽象类的成员修饰可以自定义。
接口中的成员修饰符是固定的。全是public的
三 多态
多态:函数本身就具备多态性,某一种事物有不同的具体的体现。
体现:父类引用或者接口的引用指向了自己的子类对象。//Animal a = new Cat();
多态好处:提高了程序的扩展性。
多态的弊端:当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。访问的局限性。
匿名内部类:没有名字的内部类。就是内部类的简化形式。一般只用一次就可以用这种形式
匿名内部类的格式:new 父类名&接口名(){定义子类成员或者覆盖父类方法}.方法。
异常:
异常处理原则:功能抛出几个异常,功能调用如果进行try处理,需要与之对应的catch处理代码块,这样处理有针对性,抛几个处理几个。
特殊情况:try对应多个catch时,如果有父类的catch语句块,一定要放在最下面。
throw 和 throws关键字的区别:
throw用于抛出异常对象,后面跟是异常对象;throw用在函数内。
throws用于抛出异常类,后面跟的异常类名,可以跟多个,用逗号隔开。throws用在函数上。
2 多线程
线程的2种创建方式
创建线程的第一种方式:继承Thread,由子类覆写run方法。
class Th1
{
public static void main(String[] args)
{
Thread1 th = new Thread1();
th.start();
}
}
class Thread1 extends Thread
{
public void run()
{
//线程执行的内容
}
}
线程状态:
新建:start()
运行:具备执行资格,同时具备执行权。
冻结:sleep(time),wait()--notify()唤醒;线程释放了执行权,同时释放执行资格。
临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权。
消亡:Stop()
创建多线程第二种方式:实现一个接口 Runnable。
class Th2
{
public static void main(String[] args)
{
Thread2 t = new Thread2();
Thread th = new Thread(t);
th.start();
}
}
public Thread2 implements Runnable
{
public void run()
{
//线程执行内容
}
}
多线程安全问题的原因:
一个线程在执行多条语句时,并运算同一个数据时,在执行过程中,其它线程参与进来,并操作了这个数据。导致错误数据的产生。
解决安全问题的原理:
只要将操作的共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他线程不能进来执行就可以解决这个问题。
java中提供了一个解决方案方式:就是同步代码块。
格式:
Synchronized(对象){//对任意对象都可以,这个对象就是锁
需要被同步的代码;
}
wait 和sleep区别:
wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者 notigyAll来唤醒。
sleep:必须指定时间,时间到自动从冻结状态转成运行状态。
wait:线程会释放执行权,而且线程会释放锁。
Sleep:线程会释放执行权,但不会释放锁。
同步嵌套同步代码块会产生死锁。
4 集合框架
常用的ArrayList HashSet
Collection:
|--List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。
|--ArrayList:底层的数据结构是数组,线程不同步,ArrayList代替了Vector 。查询速度很快。
|--LinkedList:底层的数据结构是链表,线程不同步,增删很快。
|--Vector:底层的数据结构是数组,线程同步,查询增删都很慢。
1,添加:
add(index,element)
bool addAll(index,collection)
2,删除
E remove(index)
3,获取
int get(index)
int indexOf(obj)
int lastIndexOf(obj)
List<E> subList(start,end)
4,修改:
E set(index,element)
5,获取所有元素
ListIterator listIterator():list集合特有的迭代器。(具备了对元素的增、删、改、查)
List集合因为角标有了自己的获取元素的方式:遍历。
for(int x=0;x<list.size();x++){
System.out.println("get:"+list.get(x));
}
|--Set:无序,不可以存储重复元素。必须保证元素的唯一性。
1,添加
add(object)
addAll(Collection)
2,删除
clear()
remove(obj)
removeAll(collection)
3,判断
boolean contains(obj)
boolean containsAll(collection)
boolean isEmpty()
4,获取:
int size()
5,取交集:
boolean retainAll(collection)//仅保留 set 中那些包含在指定 collection 中的元素(可选操作)。
6,获取集合中所有元素:
Iterator iterator()
7,将集合变成数组:
toArray();
|--HashSet:底层数据结构是哈希表,线程不同步,无序,高效。
HashSet集合保证元素唯一性:通过元素的hashCode方法,和equals方法完成的,当元素的hashCode值相同时,才继续判断元素equals是否为true。如果为true,那么视为相同元素,不存。如果为false,那么存储。在原来对象的哈希值基础+1顺延。
|--LinkedHashSet:有序,HashSet的子类。
|--TreeSet:对Set集合中的元素的惊醒指定顺序的排序。不同步。TreeSet底层的结构是二叉树。
哈希表原理:
1,对对象元素中的关键字(对象中特有的数据),进行哈希算法运算,并得出一个算法值,这个值称为哈希值。
2,哈希值就是这个元素的位置。
3,如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同 ,不存储,因为元素重复,如果相同,就存储,在原来对象的哈希值基础+1顺延。
4,存储哈希值的结构,我们称为哈希表。
5,既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象关键字是唯一,这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。
TreeSet:
对于Set集合进行元素的指定顺序排序,排序需要依据元素自身具备的比较性。如果元素不具备比较性,在运行时会发生ClassCastException异常。
所以需要元素实现Comparable接口,强制让元素具备比较性,复写compareTo方法的返回值,确定元素在TreeSet数据结构中的位置。
TreeSet方法保证元素唯一性的方式:就是参考比较方法的结果是否为0,如果return 0,视为两个对象重复,不存。
注意:在进行比较时,如果判断元素不唯一,比如哦,同姓名,同年龄,才视为同一个人。在判断时,需要分主要条件和次要条件,当主要条件相同时,再判断次要条件,按照次要条件排序。
TreeSet集合排序有两种方式,Comparable和Comparator区别:
1:让元素自身具备可比较性,需要元素对象实现Comparable接口,覆盖compareTo方法。
2:让集合自身具备可比较性,需要定义一个实现了Comparator接口的比较器,并覆盖compare方法,并将该类对象作为实际参数传递给TreeSet集合的构造函数。
------------------------------------------------------------------------------------------------------------------------------------------------
Map集合:
|--Hashtable:底层是哈希表数据结构,是线程同步。不可以存储null键 null值
|--HashMap:底层是哈希表数据结构,是线程不同步的。可以存储null键 null值,代替了Hashtable.
|--TreeMap:底层是二叉树结构,可以对map集合中的键进行指定的顺序的排序。
Map集合存储和Collection有着很大的不同:
Collection一次存一个元素;Map一次存一对元素。
Collection是单列集合;Map 是双列集合。
Map中的存储的一对元素:一个键,一个是值,键与值之间有对应关系
特点:要保证map集合中键的唯一性。
1,添加。
put(key,value)
putAll(Map)
2,删除。
clear()
value remove(key);
3,判断
boolean isEmpty()
boolean containsKey(key)
boolean contansValue(value)
4,取出。
int size()
value get(key)
Collection values();
5,想要获取map中所有元素:
原理:map中没有迭代器的,collection具备迭代器,只要将map集合转成Set集合,可以使用迭代器了。之所以转成set,是因为map集合具备这键的唯一性,其实set来自map,set集合底层其实用的就是map方法。
把map集合转成set的方法:
Set keySet();
Set entrySet();
Entry就是Map接口中的内部接口;
为什么要定义在map内部呢?entry是访问键值关系的入口,是map的入口中的键值对。
----------------------------------------------------------------------------------------------------------
取出map集合中所有元素的方式一:keySet()方法。
Set keySet = map.keySet();
Iterator it = keySet.iterator();
while(it.hasNext()){
Object key = it.next();
Object value = map.get(key);
System.out.println(key+":"+vlalue);
}
------------------------------------------------------------------------------------------------------------
取出map集合中所有元素的方式二:entrySet()方法。
Set entrySet = map.entrySet();
Iterator it = entrySet.iterator();
while(it.hasNext()){
Map.Entry me = (Map.Entry)it.next();
System.out.println(me.getKey()+":"+me.getValue());
}
----------------------------------------------------------------------------------------------
使用集合的技巧
看到Array就是数组结构,有角标,查询速度很快。
看到link就是链标结构,增删速度快,而且有特有的方法。addFirst();addLast();removeFirst();removeLast();getFirst();getLast();
看到hash就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到该集合元素必须覆盖hashCode,equals方法。
看到tree就是二叉树,就要想到排序,就想要用到比较。
比较的两种方式:
一个是Comparable:覆盖compareTo方法;
一个是Comparator:覆盖compare方法。
LinkedHashSet,LinkedHashMap:这两个集合可以保存哈希表存入顺序和取出顺序一致,保证哈希表有序。
集合什么时候用?
当存储的是一个元素时,就用Collection。当存储对象之间存在映射关系时,就使用Map集合。保证唯一,就用Set,不保证唯一,就用List。
-----------------------------------------------------------------------------------------------------------
Coollections:它的出现给集合操作提供了更多的功能。这个类提供的都是静态方法。
静态方法:
Collections.sort(list);
Collections.sort(list,new ComparatorByLen());//指定比较器方法
class ComparatorByLen implements Comparator<String>{
jpublic int compare(String s1,String s2){
inte temp0 = s1.length()-s2.length();
return tem==0?s1.compareTo(s2):temp;
}
}
Collections.max(list)
int index = Collections.binarySearch(list,"zz");//二分查找,放回角标。
Collection.reverseOrder();
Collection.shuffle(list);
将非同步集合转成同步集合的方法:Collections中的 XXX synchronizedXXX(XXX);
List synchronizedList(list);
Map synchronizedMap(map);
原理:定义一个类,将集合所有的方法加同一把锁后返回。
Collection和Collections的区别:
Collections是个java.util下的类,是针对集合类的一个工具类,提供一系列静态方法,实现对集合的查找,排序,替换,线程安全化 等。
Collection是个java.util下的接口,他是各种集合结构的父接口,继承于它的接口主要有Set和List,提供了关于集合的一些操作,如插入、删除、判断一个元素是否是其成员、遍历等。
------------------------------------------------------------------------------------------------------
Arrays:
用于操作数组对象的工具类,里面都是静态方法。
asList(arr):将数组转换成list集合。
String[] arr = {"aa","bbb"};
List<String> list = Arrays.asList(arr);
将数组转换成集合,有什么好处呢?
可以通过list集合中的方法来操作数组中的元素:isEmpty()、contains、indexof、set;;
主要(局限性):数组是固定长度,不可以使用集合对象增加或者删除等,会改变数组长度的功能方法。比如add、remove、clear
集合变数组:用的Collection接口中的方法:toArray();
将集合变成数组后又什么好处?限定了对集合中的元素进行增删操作,只要获取这些元素即可。
---------------------------------------------------------------------------------
Java 1.50新特性
Jdk1.50新特性:
Collection在jdk1.5后,有了一个父接口Iterable,这个接口的出现将iterator方法进行抽取,提高了扩展性。
---------------------------------------------------------------------------------------
增强for循环
fro(元素类型 变量名 :Collection 集合 & 数组){
..................
}
-------------------------------------------------------------
可变参数(...):
用到函数的参数上,当药操作一个类型元素个数不确定的时候,可是用这个方式,这个参数可以接受任意个数的同一个类型的数据。
public class VarArgsTest {
public void print(String... args) {
for (int i = 0; i < args.length; i++) {
out.println(args[i]);
}
}
public void print(String test) {
out.println("----------");
}
public static void main(String[] args) {
VarArgsTest test = new VarArgsTest();
test.print("hello");
test.print("hello", "alexia");
}
}
------------
hello
alexia
------------------------------------------------------
枚举:关键字enum
问题:对象的某个属性的值不能是任意的,必须为固定的一组取值其中的某一个;
jdk1.5中新定义了枚举类型,专门用于解决此类问题;
枚举就是一个特殊的java类,可以定于属性、方法、构造函数、实现接口、继承类;
---------------------------------------------------------------------------
自动拆装箱:java中数据类型分为两种:基本数据类型 引用数据类型(对象)
在java程序中所有的数据都需要当作对象来处理,针对8种基本数据类型提供了包装类,如下:
int-->Integer
byte-->Byte
short-->Short
long-->Long
char-->Character
double-->Double
float-->Float
boolean-->Boolean
基本数据类型和包装类型之间需要互转:
基本---引用 Integer x= new Integer(x)
引用---基本 int num = x.intValue();
1,Integer x= 1; x=x+1;经历了什么过程?装箱->拆箱->装箱;
2,为了优化,虚拟机为包装类提供了缓冲池,Integer池的大小-128~127 一个字节的大小;
3,String池:java为了优化字符串操作 提供了一个缓冲池;
---------------------------------------------------------------------------------
泛型:jdk1.5版本后出现的一个安全机制。表现格式:<>
好处:
1:将运行时期的问题ClassCastException问题转换成了编译失败,体现在编译时期,程序员就可以解决问题。
2:避免了强制转换的麻烦。
只要带有<>的类或者接口,都属于带有类型参数的类或者接口,在使用这些类或者接口时,必须给<>中传递一个具体的引用数据类型。
泛型技术:其实应用在编译时期,是给编译器使用的技术,到了运行时期,泛型就不存在了。为什么?因为泛型的擦除:也就是说,编辑器检查了泛型的类型正确后,在生成的类文件中是没有泛型的。
在运行时,如何知道获取的元素类型而不用强转呢?
泛型的补偿:因为存储的时候,类型已经确定了是同一个类型的元素,所以在运行时,只要获取到该元素的类型,在内部进行一次转换即可,所以使用者不用再做转换动作了。
什么时候用泛型呢?
当类中的操作的引用数据类型不确定的时候,以前用的Object来进行扩展的,现在可以用泛型来表示。这样可以避免强转的麻烦,而且将运行问题转移到编译时期。
----------------------------------------
泛型程序中的体现:
//泛型类:将泛型定义在类上。
class Tool<Q>
{
private Q obj;
public void setObject(Q obj)
{
this.obj = obj;
}
public Q getObject(){
return obj;
}
}
//当方法操作的引用数据类型不确定的时候,可以将泛型定义在方法上。
public <W> void method(W w){
System.out.println("method:"+w);
}
//静态方法上的泛型:静态方法无法访问类上定义的泛型。如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
public static <Q> void function(Q t){
System.out.println("function:"+t);
}
//泛型接口
interface Inter<T>
{
void show(T t);
}
class InterImpl<R> implements Inter<R>
{
public void show(R r)
{
System.out.println("show:"+r);
}
}
--------------------------------------------------------------------------
泛型中的通配符:可以解决当具体类型不确定的时候,这个通配符就是?;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用?通配符来表示未知类型。
泛型限定:
上限: ? extends E :可以接收E类型或者E的子类类型对象。
下限: ?super E:可以接收E类型或者E类型的父类类型对象。
上限什么时候用:往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类类型对象。为什么?因为取的时候,E类型即可以接收E类对象,又可以接收E的子类型对象。
下限什么时候用:当从集合中获取元素进行操作的时候,可以用当前元素的类型接收,也可以用当前元素的父类型接收。
泛型的细节:
1、泛型到底代表什么类型取决于调用者传入的类型,如果没传,默认是Object类型;
2、使用带泛型的 类创建对象时,等式两边指定的泛型必须一致;
原因:编译器检查对象调用方法时只看变量,然而程序运行期间调用方法时就要考虑对象具体类型了;
3、等式两边可以在任意一边使用泛型,在另一边不使用(考虑向后兼容);
ArrayList<String> al = new ArrayList<Object>();//错
//要保证左右两边的泛型具体类型一致就可以了,这样不容易出错。
ArrayList<? extends Object> al = new ArrayList<String>();
al.add("aa");//错
//因为集合具体对象中即可存储String,也可以存储Object的其他子类,所以添加具体的类型对象不合适,类型检查安全出现安全问题。 ? extends Object 代表Object的子类类型不确定,怎么能添加具体的对象呢?
public static void method(ArrayList<? extends Object> al){
al.add("abc");//错
}
//只能对al集合中的元素调用Object类中的方法,具体子类类型方法都不能用,因为子类类型不确定。
5 IO流
-----------------------------------------------------------------------------------------------------------------------------------
字符流:
Reader:用于读取字符流的抽象类。子类必须实现的方法只有read(char[],int,int)和close().
|--BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。
|--LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法setLineNumber(int)和getLineNumber(),他们可分别用于设置和获取当前行号。
|--InputStreamReader:是字节流通向字符流的桥梁:他使用指定的charset读取字节并将其解码为字符。它使用字符集可以由名称指定或显示给定。
|--FileReader:用来读取字符文件的便捷类。此类的构造方法默认字符编码。
|--CharArrayReader:
|--StringReader:
-----------------------------------------------------------
Writer:写入字符流的抽象类。子类必须实现的方法有write(char[],int,int)、flush()和close()。
|--BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
|--OutputStreamWriter:是字符流通向字节流的桥梁:可以使用指定的charset将写入流中的字符编码成字节。它使用的字符集可以由名称指定或显示给定。
|--Filewriter:用来写入字符文件的便捷类。此类的构造方法默认字符编码。
|--PrintWriter:
|--CharArrayWriter:
|--StringWriter:
-------------------------------------------------------------------
字节流:
InputStream:是标识字节输入流的所有类的超类。
|--FileInputStream:从文件系统中的某个文件中获得输入字节。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用FileReader。
|--FilterInputStream:包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
|--BufferedInputStream:该类实现缓冲的输入流
|--Stream:
|--ObjectInputStream:
|--PipedInputStream:
-----------------------------------------------
OutputStream:此抽象类是表示输出字节流的所有类的超类。
|--FileOutputStream:文件输出流是用于将数据写入File或FileDescriptor的输入流。
|--FilterOutputStream:此类是过滤输出流的所有类的超类。
|--BufferedOutputStream:该类实现缓冲的输出流。
|--PrintStream:
|--DataOutputStream:
|--ObjectOutputStream:
|--PipedOutputStream:
--------------------------------------------------
缓冲区是提高效率用的,给谁提高呢?
BufferedWriter:是给字符输出流提高效率用的,那就意味着,缓冲区对象创建时,必须要先有流对象。明确要提高具体的流对象的效率。
FileWriter fw = new FileWriter("bufdemo.txt");
BufferedWriter bufw = new BufferedWriter(fw);//让缓冲区和指定流相关联。
for(int x=0;x<4;x++){
bufw.write(x+"abc");
bufw.newLine();//写入一个换行符,这个换行符可以依据平台的不同写入不同换行符。
bufw.flush;//对缓冲区进行刷新,可以让数据到达目的地中。
}
bufw.close();//关闭缓冲区,其实就是关闭具体的流。
-------------------------------------------------
BufferedReader:
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null){//readLine方法返回的时候是不带换行符的。
System.out.println(line);
}
bufr.close();
------------------------------------------------------
//记住,只要一簇键盘录入,就用这句话。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));//输出到控制台
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase());//将输入的字符转成大写字符输出
bufw.newLine();
bufw.flush();
}
bufw.close();
bufr.close();
-------------------------------------------------------------
流对象:其实很简单,就是读取和写入。但是因为功能的不同,流的体系中提供N多的对象。那么开始时,到底该用哪个对象更为合适呢?这就需要明确流的操作规律。
流的操作规律:
1,明确源和目的
数据源:就是需要读取,可以使用两个体系:InputStream、Reader:
数据汇:就是需要写入,可以使用两个体系:OutputStream、Writer:
2,操作的数据是不是纯文本数据?
如果是:数据源:Reader 数据汇:Writer
如果不是:数据源:InputStream 数据汇:OutputStream
3,虽然确定了一个体系,但是该体系有太多的对象,到底用哪个呢?
明确操作的数据设备。
数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。
4,需要在基本操作上附加其它功能吗?比如缓冲。
如果需要就进行装饰。
转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。
转换流的最强功能就是基于 字节流+编码表。没有转换,没有字符流。
发现转换流有一个子类就是操作文件的字符流对象:
InputStreamReader
|--FileReader
OutputStreamWriter
|--FileWriter
想要操作文本文件,必须要进行编码转换,而编码转换动作转换流都完成了。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。
但是子类有个局限性,就是子类中使用编码是固定的,是本级默认的编码表,对于简体中文版的系统默认码表是GBK.
FileReader fr = new FileReader("a.txt");
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");
以上两句代码功能一致,
如果仅仅使用平台默认码表,就使用FileReader fr = new FileReader("a.txt");//因为简化。
如果需要指定码表,必须用转换流。
转换流=字节流+编码表。
转换流的子类FileReader =字节流+默认编码表
凡是操作设备上的文本数据,涉及到编码转换,必须是使用转换流。
------------------------------------------------------------------------------------------
File类:将文件系统的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据。
File类常见方法:
1,创建。
boolean createNewFile():在指定目录下创建文件,如果该文件存在,则不创建。而对操作文件的输出流而言,输出流对象已建立,就会创建文件,如果文件已存在,会覆盖。
boolean mkdir():创建此抽象路径名指定的目录。
boolean mkdirs():创建多级目录。
2,删除。
boolean delete():删除此抽象路径名表示文件或目录。
void deleteOnExit():在虚拟机退出时删除。
注意:在删除文件夹时,必须保证这个文件夹中没有任何内容,才可以将该文件夹用delete删除。
window的删除动作,是从里往外删。注意:java删除文件不走回收站。要慎用。
3,获取
long length():获取文件大小。
String getName():返回由此抽象路径名标识的文件或目录的名称。
String getPath():返回抽象路径名转换为一个路径名字字符串。
String getAbsolutePath():返回此抽象路径名的绝对路径名字符串。
String getParent():返回此抽象路径名父目录的抽象路径名,如果此路径名没有指定父目录,则返回null.
long lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。
File.pathSeparator:返回当前系统默认的路径分隔符,windows默认为“;”。
File.Separator:返回当前系统默认的目录分隔符,windows默认为"\"。
4:判断:
boolean exists():判断文件或者文件夹是否存在。
boolean isDirectory():测试此抽象路径名表示文件是否是一个目录。
boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。
boolean isHidden():测试此抽象路径名表示的文件是否是一个隐藏文件。
boolean isAbsolute():测试此抽象路径是否为绝对路径名。
5:重命名
boolean renameTo(File dest):可以实现移动的效果。剪切+重命名。
String[] list():列出指定目录下的当前的文件和文件夹的名称。包含隐藏文件。
如果用list方法的File对象中封装的是一个文件,那么list方法返回数组为null。如果封装的对象不存在也会返回null。只有封装的对象存在并且是文件夹示,这个方法才有效。
-------------------------------------------------------------------
递归:就是函数自身调用自身。
什么时候用递归呢?
当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。
简单说:功能内部又用到了该功能,但是传递的参数不确定。
递归的注意事项:
1:一定要定义递归的条件。
2:递归的次数不要过多。容易出现StackOverflowError栈内存溢出错误。其实递归就是栈内存中不端的加载同一个函数。
-------------------------------------------------------------------------------
Java.util.Properties:一个可以将键值进行持久化存储的对象。Map--Hashtable的子类
Map
|--Hashtable
|-Properties:用于属性配置文件,键和值都是字符串类型。
特点:1:可以持久化存储数据。2:键值都是字符串。3:一般用于配置文件。
load():将流中的数据加载进集合。
原理:其实就是将读取的流和指定文件相关联,并读取一行数据,因为数据是规则的key=value,所以获取一行后,通过=对该行切割,左边就是键,右边就是值,将键、值存储到properties集合中。
store():写入各个项后,刷新输出流。
list():将集合的键值数据列出到指定目的地。
----------------------------------------------------------------------------------
6 高新技术:
网络编程
网络通信要素
1、IP地址:InetAddress
网络中的设备标识。
不易记忆,可用主机名。
本地回环地址:127.0.0.1主机名:localhost。
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IPDemo {
public static void main(String[] args) throws UnknownHostException {
InetAddress ip = InetAddress.getLocalHost();
System.out.println(ip.getHostAddress());
System.out.println(ip.getHostName());
System.out.println("-----------");
// 获取其它主机的ip地址对象
ip = InetAddress.getByName("www.baidu.com");
System.out.println(ip.getHostAddress());
System.out.println(ip.getHostName());
}
}
2、端口号
用于标识进程(应用程序)的逻辑地址,不同进程的标识。
有效端口:0~65535,其中0~1024系统使用或保留端口。
P.S.
当一台计算机A向另一台计算机B发送QQ信息时,首先路由器通过数据包中的IP地址定位该信息发送到哪一台机器。然后计算机B接收到数据包后,通过数据包中的端口号定位到发送给本机的QQ应用程序。
3、传输协议
常见协议:UDP、TCP。
UDP
将数据及源和目的封装成数据包中,不需要建立连接。
每个数据包大小在限制64K内。
因为无连接,是不可靠协议。
不需要建立连接,速度快。
应用案例:QQ、FeiQ聊天、在线视频用户的都是UDP传输协议。
UDP实例
udp发送端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPSendDemo {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
System.out.println("发送端启动....");
/*
创建UDP传输的发送端。
思路:
1.建立udp的socket服务
2.将要发送的数据封装到数据包中。
3.通过udp的socket服务将数据包发送出去。
4.关闭socket服务
*/
//1.udp socket服务。使用DatagramSocket对象
//如果发送端端口未指定,就会随机分配未被使用的端口。
DatagramSocket ds = new DatagramSocket(8888);
//2.将要发送的数据封装到数据包中。
String str = "udp 传输演示,哥们来了!";
//使用DatagramPacket将数据封装到该对象包中。
byte[] buf = str.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length,InetAddress.getByName("192.168.1.110"),10000);
//3.通过udp的socket服务将数据发送出去,使用send方法。
ds.send(dp);
//4.关闭资源
ds.close();
}
}
udp接受端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPReceDemo {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
System.out.println("接收端启动");
/*
* 建立UDP接收端的思路
* 思路:
* 1.建立udp的socket服务,因为是要接收数据,必须要明确一个端口号。
* 2.创建数据包,用于存储接收到的数据,方便用数据包对象的方法解析这些数据。
* 3.使用socket服务的receive方法将接收的数据存储到数据包中。
* 4.通过数据包的方法解析数据包中的数据。
* 关闭资源。
* */
//1.建立udp socket服务
DatagramSocket ds = new DatagramSocket(10000);
//2.创建数据包
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//3.使用接收方将数据存储到数据包中。
ds.receive(dp);//阻塞式的。
//4.通过数据包对象方法,解析其中的数据,比如:地址,端口,数据内容
String ip = dp.getAddress().getHostAddress();
//获取的端口号是发送端的端口号。
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+":"+text);
//5.关闭资源
ds.close();
}
}
TCP
建立连接,形成传输数据通信。
在连接中进行大数据量传输。
通过三次握手完成连接,是可靠协议。
必须建立连接,效率稍低。
应用案例:FTP、File Transfer Protocol(文件传输协议)
TCP实例
tcp客户端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientDemo {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1",10002);
OutputStream out = socket.getOutputStream();
out.write("tcp演示:哥们又来了".getBytes());
//读取各户端返回的数据,使用Socket读取流。
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
socket.close();
}
}
tcp服务端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10002);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(ip+":"+text);
//使用客户端socket对象的输出流输出流给客户端返回数据
OutputStream out = s.getOutputStream();
out.write("收到".getBytes());
s.close();
ss.close();
}
}
反射
反射技术:其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员。简单说:反射技术可以对一个类进行解剖。
反射的好处:大大的增强了程序的扩展性。
反射的基本步骤:
1、获取Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建的对象。
获取这个Class对象,有三种方式:
1:通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。
2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。
前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。
3:使用的Class类中的方法,静态的forName方法。
指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。
//1.根据给定的类名来获得 用于类加载
String classname = "cn.itcast.reflect.Person";//来自配置文件
Class class = Class.forName(classname);//此对象代表Person.class
//2.如果拿到了对象,不知道是什么类型 用于获得对象的类型
Object obj = new Person();
Class clazz1 = obj.getClass();//获得对象具体的类型
//3.如果是明确地获得某个类的Class对象 主要用于传参
Class clazz2 = Person.class;
反射的用法:
1、需要获得java类的各个组成部分,首先要获得类的Class对象,获得Class对象的三种方式:
Class.forName(classname) 用于做类的加载
obj.getClass() 用于获得对象的类型
类名.class 用于获得指定的类型,传参用
2、反射类的成员方法:
Class clazz = Person.class;
Method method = clazz.getMethod(String name,Class<?>... parameterTypes);
method.invoke(Object obj,Object... args);//obj是该类对象
3、反射类的构造函数:
实例化有参数的构造函数
Constructor con = clazz .getDeclaredConstructor(Class<?>... parameterTypes)//通过字节码文件获取到构造器
con.newInstance(Object... initargs)//使用构造器实例化对象
实例化无参的构造函数
Object o = clazz.newInstance();
4、反射类的属性:
Field filed = clazz.getDeclaredField(String name);
filed.setAccessible(true);
filed.set(Object obj,Object value);
获取了字节码文件对象后,最终都需要创建指定类的对象:
创建对象的两种方式(其实就是对象在进行实例化的初始化方式):
1,调用空参数的构造函数:使用了Class类中的newInstance()方法。
2,调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过构造函数的对象的newInstance(实际参数)进行对象的初始化。
综上所述,第二种方式,必须要先声明具体的构造函数的参数类型,不便于扩展。所以一般情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数。
-------------------------------------------------------------------------
//如何生成获取字节码文件对象的实例对象。
Class clazz = Class.forName("cn.itcast.bean.Person");//类加载
//直接获得指定的类型
clazz = Person.class;
//根据对象获得类型
Object obj = new Person("zhangsan",19);
clazz = obj.getClass();
Object obj = clazz.newInstance();//实例化对象的方法调用就是指定类中的空参数构造函数,给创建的对象进行初始化。
//如果没有空参数的构造函数,那么只要获取指定参数的构造函数,用该类函数来进行实例化。
//获取一个带参数的构造器
Constructor constructor = clazz.getConstructor(String.class,int.class);
//想要对对象进行初始化,使用构造器的方法newInstance();
Object obj = constructor.newInstance("zhangsan",30);
//获取所有构造器。
Constructor[] constructors = clazz.getConstructors();//只包含公共的
constructors = clazz.getDeclaredConstructors();//包含私有的
for(Constructor con : constructors){
System.out.println(con);
}
-------------------------------------------------------------------------------------
反射指定类中的方法:
//获取类中所有的方法。
Class clazz = Class.forName("cn.itcast.bean.Person");
Mehtod[] methods = class.getMethods();//获取的是该类中的共有方法和父类中的共有方法。
methods = clazz.getDeclareMethods();//获取本类中的方法,包含私有方法。
fro(Method method : methods){
System.out.println(method);
}
//获取指定方法:
Class clazz = Class.forName("cn.itcast.bean.Person");
Method method = clazz.getMethod("show",int.class,String.class);//获取指定名称的方法。
Object obj = clazz.newInstance();
method.invoke(obj,39,"hehehe");//执行一个方法
//想要运行私有方法。
Class clazz = Class.forName("cn.itcast.bean.Person");
//想要获取私有方法。必须用getDeclearMethod();
Method method = clazz.getDeclaredMethod("method",null);
//私有方法不可以直接访问,因为权限不够。非要访问,可以通过暴力的方式。
method.setAccessible(true);//一般很少用,因为私有就是隐藏起来,所以尽量不要访问。
//反射静态方法。
Class clazz = Class.forName("cn.itcast.bean.Person");
Method method = clazz.getMethod("function",null);
method.invoke(null,null);
-----------------------------------------------------------------------------------
正则
正则表达式:其实是用来操作字符串的一些规则。
好处:正则的出现,对字符串复杂操作变得更为简单。
特点:将对字符串操作的代码用一些符号来表示。只要使用了指定的符号,就可以调用底层的代码对字符串进行操作。符号出现,简化了代码的书写。
弊端:符号的出现虽然简化了书写,但是却降低了阅读性。其实更多是用正则解决字符串操作问题。
组:用小括号表示,没定义一个小括号,就是一个组,而且有自动编号,从1开始。只要使用组,对应的数字就是使用该组的内容。别忘了,数组要加\\。
常见操作:
1,匹配:其实用的就是String类的matches方法。
String reg="[1-9][0-9]{4,14}"
boolean b = qq.matches(reg);//将正则和字符串关联对字符串进行匹配。
/*
* 演示匹配
* */
public static void functionDemo_1()
{
//匹配手机号码是否正确
String tel = "15800022335";
String regex = "1[358]\\d{9}";
boolean b = tel.matches(regex);
System.out.println(tel+":"+b);
}
2,切割:其实用的就是String类中的split方法。
/*
* 演示切割
* */
public static void functionDemo_2()
{
String str = "zhangsna lisi wangwu";
String[] names = str.split(" +");
for(String name : names)
{
System.out.println(name);
}
}
3,替换:其实用的就是String类中replaceAll();
/*
* 演示替换
* */
public static void functionDemo_5()
{
String str = "abbbbcd11111efg";
//$表示前一个参数的第一组
str= str.replaceAll("(.)\\1+", "$1");
System.out.println(str);
}
4,获取:
a,先要将正则表达式编译成正则对象。使用的是Pattern中静态方法compile(regex);
b,通过Pattern对象获取Matcher对象。Pattern用于描述正则表达式,可以对正则表达式进行解析。而将规则操作字符串,需要从新封装到匹配对象Matcher中。然后使用Matcher对象的方法来操作字符串。
如何获取匹配器对象呢?
通过Pattern对象中的matcher方法。该方法可以正则规则和字符串相关联。并返回匹配器对象。
c,使用Matcher对象中的方法即可对字符串进行各种正则操作。
/*
* 演示获取
* */
public static void functionDemo_7()
{
String str = "da jia hao,ming tiam bu fang jia";
//\\表示单词边界
String regex = "\\b[a-z]{3}\\b";
//1.将正则封装成对象
Pattern p = Pattern.compile(regex);
//2.通过正则对象获取匹配器对象
Matcher m = p.matcher(str);
//使用Matcher对象的方法对字符串进行操作
//既然要获取三个字母组成的单词
//查找:find()
while(m.find())
{
System.out.println(m.group());
System.out.println(m.start()+":"+m.end());
}
}
------------------------------------------------------------------------------------