您的位置:首页 > 编程语言 > Java开发

java基础学习笔记

2016-01-24 10:13 429 查看
第一部分:java实现面向对象编程

运算符分类:

算术运算符:+ - * / (加减乘除)

关系运算符:< ,>,<=,=>,==,!=

逻辑运算符:!,&&,||

优先级:算术运算符>关系运算符>逻辑运算符

注:逻辑非的优先级比乘除的还要高

第六章:类和对象

static(静态)关键字:

1:用法:是一个修饰符,用于修饰成员(成员变量、成员方法),当成员被静态修饰后,就多了一种调用方式,除了可以被对象调用外,还可以被类名直接调用(类名.静态成员)。

2:静态的特点:

①随着类的加载而加载,随着类的消失而消失,说明它的生命周期最长

②优先对象的存在,需要明确一点:静态优先对象的存在

③可以被所有对象共享

④可以被类名直接调用

实例变量(成员变量)和类变量(静态变量)的区别:

①存放位置:类变量随着类的加载而存在于方法区中;实例变量随着对象的建立而存在于堆内存中。

②生命周期:类变量的生命周期最长,随着类的消失而消失;实例变量的生命周期随着对象的消失而消失。

静态(static)使用注意事项:

①静态方法只能访问静态成员,非静态方法既可以访问静态也可以访问非静态。

②静态方法中不可以定义this 、super关键字,因为静态优先于对象存在,所以静态方法不可出现this。

③主函数是静态的。

静态(static)的利弊:

利:①对对象的共享数据进行单独空间的存储,节省空间,没必要每一个对象都存储一份。②可以直接被类名调用。

弊:①生命周期过长 ②访问出现局限性(静态虽好,但静态只能访问静态)

分析主函数public static void main (String [] args)

主函数:是一个特殊的函数,作为程序的入口,可以被JVM调用

主函数的定义:

public:代表着该函数的访问权限是最大的。

static:代表主函数随着类的加载就已经存在了。

void:主函数没有具体的返回值。

main:不是关键字,但是是一个特殊的单词,可以被JVM识别

(String [] args):函数的参数,参数的类型是一个数组,该数组中的元素是字符串,即字符串类型的数组。

注:主函数是固定格式的,JVM在调用的时候时,传入的是new String [0]。

什么时候使用静态?static

要从两个方面下手:因为静态修饰的内容有成员变量和函数!

什么时候定义静态变量(类变量)呢?

当对象中出现共享数据时,该数据被静态修饰,对象中的特有数据要定义成非静态,存在于堆内存中。

什么时候定义静态函数呢?

当函数功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的。

静态(static)的应用:

每一个应用程序中都有共性功能,可以将这些功能进行抽取、独立封装,以便服用。

构造函数的权限:一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致,如果类被public修饰,那么默认的构造函数也带public修饰,如果类没有被public修饰,那么默认构造函数也没有public修饰。即:默认构造函数的权限随着类的变化而变化。

第七章:继承和多态

继承:

继承的优点:

①提高代码的复用性

②让类与类之间产生了关系,有了这个关系才有了多态的特性。

注:①千万不要为了获取其他类的功能简化代码而继承,必须是类与类之间有所属关系才可以继承,所属关系是 is—a ②java语言中只支持单继承,不支持多继承。因为多继承容易带来安全隐患:当多个父类定义了相同的功能,当功能的内容不相同时,子类不确定要运行哪一个……(C++支持多继承)。③java支持多层继承,也就是一个继承体系

如何使用一个继承体系中的功能呢?

想要使用体系,应先查阅体系父类的描述,因为父类中定义的是该体系中共性功能。通过了解共性功能,就可以知道该体系的基本功能,那么这个体系已经可以基本使用了。

在具体调用时,要创建最子类的对象,为什么呢?

①因为有可能父类不能创建对象(如抽象类、接口)

②创建子类可以使用更多的功能,包括基本的也包括特有的。

总结:查阅父类功能,创建子类对象使用功能。

子类、父类出现后类成员(变量、函数、构造函数)的特点:

1:变量:如果子类中出现非私有的同名成员变量时。子类要访问本类中的变量时用this;子类要访问父类中同名变量用super。super的使用和this的使用几乎一致:

this代表的是本类对象的引用;super代表的是父类对象的引用

2:子类中的函数:当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容。如同父类的函数被覆盖了一样。即:覆盖(重写)

重写(覆盖):当子类继承父类,沿袭了父类的功能到子类中,但是子类虽具备该功能,但功能的内容却和父类不一致,这时没必要定义新功能,而且使用覆盖的特性,保留父类的功能定义,并重写功能内容。

覆盖(重写)满足的要求:

①子类和父类必须具备相同的方法名

②必须具备相同的参数列表

③必须具有相同的返回值类型

④子类覆盖父类,必须保证子类的权限大于等于父类权限才可以覆盖,否则编译失败

⑤静态只能覆盖静态

注意重写与重载的区别:

重载:在同一个类中,方法名相同,参数列表不同,与返回值无关。

3:子父类中的构造函数:在对子类初始化时,父类的构造函数也会运行。因为子类的构造函数默认第一行有一条隐式的语句super();super()会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super()。

为什么子类一定要访问父类中的构造函数?

答:因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的,所以子类在初始化的时要先访问一下父类中烦人构造函数。如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。

注意:super语句一定定义在子类构造函数的第一行。

结论:子类的所有构造函数,默认都会访问父类中的空参数的构造函数,因为子类每一个构造函数内的第一行都有一句super()。

当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来访问父类中的构造函数,当然:子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。子类中至少会有一个构造函数访问父类中的构造函数。

多态:可以理解为事物存在的多种体现形态。

1:多态的体现:①父类引用指向自己的子类对象。②父类引用也可以接收自己的子类对象

2:多态的前提:必须是类与类之间有关系,要么继承,要么实现,通常还有一个前提:存在覆盖

3:多态的好处:打打提高了程序的扩展性

4:多态的弊端:只能使用父类的应用访问父类中的成员。

5:在多态(Fu f=new Zi();)中的成员函数的特点:

①在编译时期:参阅应用型变量所属的类中是否有调用的方法,如果有,编译通过,如果没有编译失败。

②在运行时期:参阅对象所属的类中是否有调用的方法。

总结:成员函数在多态调用时,编译看左边,运行看右边。

6:在多态(Fu f=new Zi();)中的成员变量的特点:

无论编译还是运行,都参考左边。(引用型变量所属的类)

7:在多态(Fu f=new Zi();)中,静态成员函数的特点:

无论编译还是运行,都参考左边。(引用型变量所属的类)

final关键字:

1:可以修饰类、函数、和变量。

2:被final修饰的类不可以被继承。

3:被final修饰的方法不可以被重写。

4:被final修饰的变量是一个常量,只能赋值一次,既可以修饰成员变量,又可以修饰局部变量。当在描述事物的时,一些数据的出现是固定的,那么这时为了增强阅读性,那么将这些值定义为常量。

常量的书写规则:所有字母都要大写,如果由多个字母组成,单词之间用“_”连接

5:内部类定义在类中的局部位置上时,只能访问改局部被final修饰的局部变量。

第八章:抽象类和接口

抽象类:当多个类中出现相同功能,但功能主体不同时,这时可以向上抽取,只抽取功能定义(即:函数名),而不抽取功能主体。

特点:

1:抽象方法一定在抽象类中。

2:抽象类和抽象方法必须被abstract关键字修饰。

3:抽象类不可以用new创建对象,因为调用抽象方法没有意义。

4:抽象类中的方法要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用,如果子类只覆盖了部分抽象方法,那么子类还是一个抽象类。

抽象类和一般的类没有太大的不同:

1:抽象类该如何描述事物就如何描述事物,只不过该事物出现了一些看不懂的东西,这些不确定的部分也是事物的功能,需要明确的出现,但无法定义主体,故通过抽象方法来表示。

2:抽象类比一般的类多了个抽象函数,就是在类中可以定义抽象方法,但抽象类不能实例化。

特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。

什么是模板方法呢?

答:在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去,由该类的子类去完成。

接口:可以认为是一个特殊的抽象类,当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。

class:用于定义类。interface:用于定义接口。

接口定义时的格式特点:

1:接口中只包括:常量、抽象方法。

2:接口中的成员都是固定修饰符:

常量:public static final

方法:public abstract

总结: ①接口中的成员都是public的 ②接口是不可以创建对象的,因为有抽象方法,需要被子类实现,子类对接口中的所有抽象方法全部重写后,子类才可以实例化,否则子类是一个抽象类。

接口的特点:

1:接口是对外暴露的规则。

2:接口是程序的功能扩展。

3:接口可以用来实现。

4:类与接口之间是实现关系,而且可以继承一个类的同时实现多个接口。

(如:class A extends B implements Inter A ,Inter B)

5:接口与接口之间可以有继承关系。

内部类:

1:内部类的访问规则:

①内部类可以直接访问外部内中的成员,包括私有。之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用。格式为:外部类名.this

②外部类要访问内部类,必须建立内部类对象。

2:内部类访问格式

①当内部类定义在外部类的成员位置上、而且非私有化时,可以在外部其他类中直接建立内部类对象。

格式:外部类名.内部类名 变量名=new 外部类( ).内部类( );

例如:Outer.Inner in=new Outer().Inner();

②当内部类在成员位置上时,就可以被成员修饰符修饰。

比如:private :将内部类在外部类中进行封装;static:内部类就具备了static的特性,当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限性。

在外部其他类中如何直接访问static内部类的非static成员呢?

答:new Outer.Inner().function

在外部其他类中如何直接访问static内部类的static成员呢?

答:Outer.Inner.function

注意:①当内部类中定义了静态成员,则该内部类必须是static的。

②当外部类中的静态方法访问内部类时,内部类也必须是静态的。

什么时候使用内部类呢?

答:当描述事物时事物的内部还有事物,则该事物用内部类来描述,因为内部事物在使用外部事物的内容。

内部类定义在局部时:

①不可以被成员修饰符修饰(private、static)

②可以直接访问外部类中的成员,因为还持有外部类的引用,但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量。

匿名内部类:

1:匿名内部类其实就是内部类的简写格式。

2:定义匿名内部类的前提:内部类必须继承一个类或者实现一个接口。

3:匿名内部类的格式:

new 父类或者接口(){

定义子类的内容

};

4:其实匿名内部类就是一个匿名子类对象,可以理解为是带内容的对象。

5:匿名内部类中定义的方法最好不要超过3个。

第九章:异常

异常:

1:异常的定义:就是程序在运行时出现不正常情况。

2:异常的由来:问题也是现实生活中的一个具体的事物,也可以通过java的类的形式进行描述,并进行封装对象,其实就是java对不正常情况进行描述后的对象体现。

3:异常的划分:①一种是严重的问题 ②一种是非严重的问题

严重的问题:java通过error类进行描述,对于error一般不编写针对性的代码对其进行处理。

非严重的问题:java通过Exception类进行描述,对于Exception可以使用针对性的处理方式进行处理。

注:无论是error还是exception都具有一些共性内容。比如:不正常情况的信息、引发的原因等。

4:异常的处理:java提供了特有的语句进行处理。

try{

//需要被检测的代码

}catch(异常类 变量){

//处理异常的代码;(处理方式)

}finally{

//一定会执行的语句

}

5:对捕获到的异常对象进行常见方法操作。

String getMessage();获取异常信息

6:在有可能出现的问题上声明异常,便于提高安全性,被调用时必须要进行处理,若不处理则编译失败。

7:对多异常的处理。

①声明异常时,建议声明更为具体的异常,这样处理就可以更具体。

②对方声明几个异常就对应几个catch块,不要定义多余的catch块,如果多个catch块中的异常出现继承关系,则父类异常的catch块应放在最下面。

③在进行catch处理时,catch中一定要定义具体处理方式,不要简单定义一句e.PrintStackTrace();也不要简单的就书写一条输出语句。

8:自定义异常:

因为项目中会出现特有的问题,而这些问题并未被java所描述和封装对象,所以对于这些特有的问题,可以按照java对问题封装的思想,将特有的问题进行自定义的异常封装。

①当在函数内部出现了throw抛出异常对象,那么就必须对给出对应的处理动作,要么在内部catch处理,要么在函数声明上让调用者去处理。

一般情况下:函数内部出现异常,函数上需要声明。

如何自定义异常呢?

因为父类中已经把异常信息的操作都完成了,所以子类只要在构造时,将异常信息通过super语句传递给父类,那么就可以直接通过getMessage方法获取自定义的异常信息。

注:自定义异常必须要继承exception

继承exception的原因?

答:异常体系有一个特点:因为异常类和异常对象都可被抛出,他们都具有可抛性,这个可抛性是throwable这个体系中独有的特点,只有这个体系中的类和对象才可以被throws和throw操作。

throws和throw的区别:

①作用不同:throw用于程序员自行产生并抛出异常;throw用于声明该方法内抛出了异常。

②使用的位置不同:throw用于方法体内部,可以作为单独语句使用,throws跟在方法参数列表的后面,不能单独使用。

③内容不同:throw抛出一个异常对象,且只能是一个;throws后面跟异常类,且可以分多个异常类。

9:exception中有一个特殊的子类异常RuntimeException运行异常。

①如果在函数内容抛出异常,函数上可以不用声明,编译一样可以通过。

②如果在函数上声明了该异常,调用者可以不用处理,编译一样通过。

之所以不需要在函数上声明,是因为不需要让调用者处理;当该异常发生时,希望程序停止,因为在运行时,出现了无法继续运算的情况,希望停止程序后,对代码进行修正。

10:自定义异常时:如果该异常的发生导致无法再继续进行运算,就让自定义异常继承RuntimeException

11:异常分两种:

①编译时被检测的异常。

②编译时不被检测的异常(即运行时的异常,RuntimeException以及其子类)。

finally 代码块:

是一定执行的代码,通常用于关闭资源。

记住一点:catch是用于处理异常,如果没有catch就代表异常没有处理过,如果该异常是检测时异常,那么必须声明。

异常在子父类覆盖中的体现:

①子类在覆盖父类时,如果父类方法抛出异常,那么子类的覆盖方法只能抛出父类的异常或者改类异常的子类。

②如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。

③如果父类或者接口的方法中没有异常的抛出,那么子类在方法覆盖时,也不可以抛出异常,如果子类方法发生了异常,就必须要进行try处理,绝对不能抛。

异常的总结:

1:异常是什么?

答:异常是对问题的描述,将问题进行对象的封装。

2:异常的体系?

3:当函数内容有throw抛出异常对象,并未进行try处理时,则必须要在函数上声明,否则编译失败。

注:RuntimeException除外,也就是说函数内抛出的是RuntimeException异常,函数上可以不声明。

4:注意:

①finally中定义的通常是关闭资源代码,因为资源必须释放。

②finally只有一种情况不会执行,即当执行到System.exit(0)时finally不会执行。

5:自定义异常:定义类必须继承Exception或RuntimeException

作用:

①:为了让该自定义类具备可抛性

②让该类具备操作异常的共性方法

③自定义异常按照java的面向对象的思想,将程序中出现的特有问题进行封装。

④异常的的好处:第一:将问题进行封装,第二:将正常的流程代码和问题处理代码相分离,方便于阅读。

⑤异常的处理原则

5.1处理的方式有两种:try或者throw。

5.2调用到抛出异常的功能时,抛出几个就处理几个。

5.3多个catch时,父类的的catch放到最下面。

5.4 catch内需要定义针对性的处理方式,不要简单的定义printStackTrace输出语句。

6:当要定义自定义异常的信息时,可以使用父类已经定义好的功能,即将异常的信息传递给父类的构造函数:

如下:

class MyException extends Exception

{

MyException (String message)

{

super(message);

}

}

7:当捕获到的异常本功能处理不了时,可以继续在catch中抛出:

try

{

throw new AException ();

}catch(AException e ){

throw e;

}

8:如果该异常处理不了,但并不属于该功能出现的异常,可以将异常转换后,再抛出和该功能相关的异常。

try

{

throw new AException ();

}catch(AException e ){

//对处理AException

throw new AException ();

}

第二部分:java高级实用技术

第一章:集合框架及泛型

为什么出现集合类?

答:面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储。集合就是存储对象最常用的一种方式。

数组和集合类同是容器,有何不同?

①数组虽然也可以存储对象,但是长度是固定的,集合的长度不是固定的。

②数组中可以存储基本数据类型,集合只能存储对象。

集合类的特点:

集合只能存储对象,集合的长度是可变的,集合可以存储不同类型的对象。

集合类的总结:

①add方法的参数类型是Object,以便于接受任意类型的对象。

②集合中存储的都是对象的引用(地址)。

什么是迭代器呢?

答:其实就是集合取出元素的一种方式

①迭代器是定义在集合内部的一种取出元素的方式。因定义在集合内部,所以就可以直接访问集合内部的元素,那么取出方式就被定义成了内部类。

②每一个容器的数据结构不同,所以取出的动作细节也不一样,但是都有共性内容:判断和取出,那么就将其共性抽取。

③这些内部类都是符合一个规则,该规则是Iterator。如何获取集合的取出对象呢?=》通过一个对外提供的方法:iterator();

注:List特有的方法:凡是可以操作角标的方法都是该体系特有的方法。

如:增:add(index,element)、addAll(index,collection);

删:remove(index)

改:set(index,element)

查:get(index); subList(from,to);listIterator();

List集合特有的迭代器 ListIterator是Iterator的子接口。

答:在迭代是,不可以通过集合对象的方法操作集合中的元素。因为会发生concurrentModificationException异常,所以在迭代器时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,只能对元素进行判断、取出、删除的操作,如果想要其他的操作,如:添加,修改等,就需要使用其子接口ListIterator

的特点:查询速度快,但是增删速度稍慢,线程不同步。

的特点:增删速度很快,但是查询速度稍慢。

LinkedList的特有方法:

addFirst();addLast();

getFirst();getLast(); 获取元素,但不删除元素,如果集合中没有元素,会出现NoSuchElementException.

removeFirst();removeLast(); 获取元素,并删除元素,如果集合中没有元素,会出现NoSuchElementException.

在JDK1.6出现了替代方法:

即: ; ;

HashSet是如何保证元素唯一性的呢?

答:是通过元素的两个方法:hashCode和equals来完成的。

如果元素的hashCode值相同,才会判断equals是否为true;

如果元素的hashCode值不同,不会调用equals。

注:对于判断元素是否存在,以及删除等操作依赖的方法是元素的hashCode和equals方法。

第二章:实用类

包(package):

1:对类文件进行分类管理。

2:给类提供多层命名空间。

3:写在程序文件的第一行。

4:类名的全称是: 包名.类名

5:包也是一种封装模式。

总结:

①包与包之间进行访问,被访问的包中的类以及类中的成员都需要被public修饰。

②不同包中的子类还可以直接访问父类中被protected权限修饰的成员。

③包与包之间可以使用的权限只有两种:public和protected。

public protected default private

同一个类中    

同一个包中   

子类  

不同包中 

④为了简化类名的书写,使用一个关键字import

import导入包中的类,建议不要写通配符*,需要用到包中的哪个类就导入哪个类。

第三章:输入/输出和反射

3.1:IO(Input、Output)流:

①IO流用来处理设备之间的数据传输。

②java对数据的操作是通过流的方式。

③java用于操作流的对象都在IO包中。

④流按操作数据分为两种:字节流、字符流。

⑤流按流向分为:输入流、输出流。

3.2 IO流常用基类:

1:字节流的抽象基类:InputStream、OutputStream

2:字符流的抽象基类:Reader、Writer

注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。

3.3 需求:在键盘上创建一个文件并写入一些文字数据:

第一步:创建一个FileWriter对象

FileWriter fw=new FileWriter(“c:\test.txt”);

该对象一旦被初始化就必须明确被操作的文件,而且该文件会被创建到指定目录下,如果该目录下已有同名文件,将会被覆盖。其实该步就是在明确数据要存放的目的地。

第二步:调用Writer方法,将字符串写入到流中

fw.Writer(“zhangxuping”);

第三步:刷新流对象中正在缓冲的数据,将数据刷到目的地中

fw.flush():

第四步:关闭流资源,但是在关闭之前会刷新一次内部缓冲的数据,将数据刷到目的地中。

fw.close();

注意:flush和close的区别

flush刷新后,流可以继续使用

close刷新后,将会关闭流。

3.4 需求:从硬盘的某个文件读取数据

第一步:创建一个FileReader对象

FileReader fr=new FileReader (“c:\test.txt”);

①创建一个文件读取流对象,和指定名称的文件相关联。

②要保证文件是已经存在的,如果不存在,会发生FileNoFoundExceptiom异常。

第二步:调用读取流对象的read方法,并打印数据

int ch=0;

while(ch=fr.read()!=-1)

{

System.out.println((char)ch);

}

注意:调对象返回的是一个整数,打印输出的时候最好将其强转为char型。

第三步:关闭流资源

fr.close();

3.5 需求:将C盘一个文本文件复制到D盘

复制的原理:其实就是将C盘下的文件数据存储到D盘的一个文件中

步骤:

1:在D盘创建一个文件,用于存储C盘文件中的数据。

2:定义读取流对象和C盘相关联。

3:通过不断的读写完成数据的存储。

4:关闭代码

注:字符写入流缓冲区:

缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须先有流对象。

操作步骤:

第一步:创建一个字符写入流对象和输出流对象

FileWriter fw=new FileWriter(“D:\copy.txt”);

FileReader fr=new FileReader(“C:\basic.txt”);

第二步:为了提高字符写入流的效率,所以加入缓冲技术,只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。

BufferedWriter bfw=new BufferedWriter(fw);

BufferedReader bfr=new BufferedReader (fr);

第三步:开始复制数据

String line=null;

while ((line=bfr.readLine())!=null)

{

fw.Writer(bfr);

bfw.flush();

}

第四步:关闭流资源

bfw.close();

bfr.close();

注:不用写fw.close()和fr.close();因为关闭缓冲区就是在关闭缓冲区中的流对象。

3.6 system:类中的方法和属性都是静态的。

out:标准的输出,默认的是控制台

in:标准的输入,默认的是键盘

3.6.1获取系统属性信息:Properties prop=System.getProperties();

因为Properties是Hashtable的子类,也就是Map集合的一个子类对象,故可以通过map方法取出该集合中的元素。

3.6.2 如何在系统中自定义一些特有的信息呢?

System.setProperty(“mykey”,”myvaule”);

3.6.3获取指定属性的信息

String vaule=System.getProperty(“os.name”);

3.7字符读取流缓冲区

该缓冲区提供了一个一次读一行的方法readLine,方便对文本数据的获取。

第一步:创建一个读取流对象,并和文件相关联

FileReader fr=new FileReader(“D:\test.txt”);

第二步:为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数。

BuffereReader bufr=new BuffereReader(fr);

第三步:读取数据,并在控制输出

String line=null;

while((line=bufr.readLine())!=null){

System.out.println(line);

}

第四步:关闭资源

bufr.close();

装饰设计模式:

当想要对已有的对象进行功能增强时,可以定义类,将已有的对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。装饰类通常会通过构造方法,接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。

装饰和继承的总结:

①装饰模式比继承要灵活,避免了继承体系臃肿,而且降低了类与类之间的关系。②装饰类因为增强已有对象具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常属于同一个体系中。

复制一个图片的思路:

①用字节读取流对象和图片相关联。

②用字节写入流对象创建一个图片文件,用于存储获取到的数据。

③循环读写,完成数据的存储

四关闭资源。

流操作的基本规律:

通过三个明确来完成

①明确源和目的

源:输入流。Inputstream Reader

目的:输出流。Outputstream Writer

②操作的数据是否是纯文本?

是:则用字符流

不是:则用字节流

③当体系明确后,再明确要使用哪个具体的对象。

通过设备来进行区分:

源设备:内存、硬盘、键盘。

目的设备:内存、硬盘、控制台

需求:把录入的数据按照指定的编码表(utf—8),将数据存到文件中。

目的:outputstream writer 是纯文本=》选writer

设备:硬盘,一个文件=》使用Filewriter

但是Filewriter是使用的默认编码表GBK ,而存储时需要加入指定的编码表utf—8,所以要使用的对象时outputwriter,而该转换流对象要接收一个字节输出流,而且还可以操作文件的字节输出流Fileoutputstream

OutputStreamWriter osw=new OutputStreamWriter(new Fileoutputstream(“d.txt”,” utf—8”));

记住转换流的使用:

转换流是字符和字节之间的转换桥梁,通常涉及到编码转换时,需要用到转换流。

File类:

①用来将文件或者文件夹封装成对象

②方便对文件和文件夹的属性信息进行操作

③File对象可以作为参数传递给流的构造函数

④了解File类中的常用方法

File类中常见的方法

1:创建

Boolean creatNewFile();在指定位置创建文件,如果该文件已经存在,则不创建,返回false,和输出流不一样,输出对象建立文件时,如果文件已经存在,则会覆盖。

Boolean mkdir();创建文件夹

Boolean mkdirs();创建多级文件夹

2:删除

boolean delete();删除失败,返回false 如果文件正在被使用,则删除不了,返回false。

void deleteOnExit();在程序退出时,删除指定文件。

3:判断

Boolean exists():判断文件是否存在

isFile();是否是文件?

isDirectory();是否是目录?

isHidden();是否是隐藏文件?

isAbsolute();是否是绝对路径?

Java重新定向标准输入/输出?

重定向输出:

PrintStream ps=new PrintStream(new FileOutputStream(“work”));

System.setout(ps);

System.out.println(“hello world”);

重新定向输入:

FileInputStream fis=new FileInputStream(“work”)

System.setIn(fis);

要求:重新定向标准I/O,实现文件的复制

重新定义标准输入

System.setIn(new FileInputStream(“E:\stu1.txt”));

重新定义标准输出

System.setOut(new PrintStream(“D:\stu2.txt”));

将字节流转换位字符流

InputStreamReader isr=new InputStreamReader(System.in);

加入缓冲技术

BufferedReader br=new BufferedReader(isr);

将字节输出流转换为字符输出流并加入缓冲技术

BufferedWriter bfw=new BufferedWriter(new OutputStreamWriter(system.out));

String line=null

while((line=isr.readLine)!=null){

bfw.writer(line);

bfw.flush();

}

关闭流资源

bfw.close();

bfr.close();

Class类:

①代表的是所有的java类,它的各个实例对象对应的是各个类在内存中的字节码

②一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类字节码是不同的,所以它们在内存中的内容是不同的。

如何得到各个字节码对应的实例对象?(Class 类型)

类名.class 例如:System.class

对象.getclass 例如:new Date().getclass()

Class.forName(“类名”) 例如:Class.forName(“java.util.Date”)

反射:就是把java类中的各个成分映射成相应的java类。

例如:一个java类中用一个class类的对象来表示,一个类中的组成部分:成员变量、成员方法、构造方法、包等等信息也用一个个的java类来表示。

①表示java类的class类提供了一系列的方法来获得其中的变量、方法、构造方法、修饰符、包等信息,这些信息就是用相应的类的实例对象来表示的。它们是Field、Method、contructor、Package等等

②一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用class类的方法可以得到这些实例对象。

constructor类:代表某个类中的一个构造方法。

①得到某个类所有的构造方法

例如:

Constructor[]constructor=class.forName(“java.lang.string”).getConstructors();

②得到某一个构造方法

Constructor constructor=class.forName(“java.lang.string”).getConstructor(StringBuffer.class);

③创建实例对象

通常方式:String str=new String(new StringBuffer(“abc”));

反射方式:String str=(String)constuctor.newInstance(new StringBuffer(“abc”));

④class.newInstance()方法

例如:String obj=(String)class.forName(“java.lang.String”).newInstance

该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。

Filed类:代表某个类中的一个成员变量。

ReflectPoint rp=new ReflectPoint(3,5);

①取出公有成员y;

Field fieldY=rp.getclass().getField(“y”);

System.out.println(fieldY.get(rp));

②取出私有成员x;

第一步:宣告x的存在:getDeclaredFiled(“x”)

第二步:允许x被取出fieldX.setAccessible(true)

Field fieldX=rp.getclass.getDeclared(“x”);

fieldX.setAccessible(true)

System.out.println(fieldY.get(rp));

Method类:代表某个类中的一个成员方法。

①得到类中的某一个方法

Method charAt=class.forName(“java.lang.String”).getMethod(“charAt”,int.class)

②调用方法

通常方式:System.out.println(str. charAt(1));

反射方式:System.out.println(charAt.invoke(str,1));

如果传递给Method对象的invoke()方法的一个参数为null,则说明该Method对象对应的是一个静态方法。

第四章:注解和多线程

进程:是一个正在执行的程序,每一个进程执行都有一个执行顺序,该顺序是执行路径或者叫一个控制单元。

线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行,一个进程至少有一个线程。

如:JVM启动的时候会有一个进程java.exe,该进程中至少有一个线程负责java程序的执行,而且这个线程的运行代码存在于main方法中,该线程称之为主线程,其实JVM启动的时候还有负责垃圾回收机制的线程。

如何在自定义的代码中自定义一个线程?

通过查找API,java已经提供了对线程这类事物的描述,就是Thread类

创建线程的第一种方式:继承Thread类

第一步:定义类继承Thread

第二步:复写Thread类中的run方法,目的是将自定义代码存储在run方法中,让线程运行

第三步:调用线程的start方法,该方法有两个作用,①启动线程②调用run方法

运行结果分析:

发现每次运行的结果都不相同。因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行,明确一点:在某个时刻只能有一个程序在运行(多核除外)cpu在做着快速的切换,已达到看上去是同时运行的效果,我们可以形象把多线程的运行形容为在互相抢夺cpu的执行权。这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长时间,cpu说的算。

为什么要覆盖run方法?

Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,也就是说Thread类中的run方法,用于存储线程要运行的代码。

线程中相关的信息:

①线程都有自己默认的名称:Thread–编号,该编号从0开始。

②static Thread currentThread() 获取当前线程对象。

③get.Name() 获取线程名称

④设置线程名称:setName 或者构造函数

创建线程的第二种方式:实现Runnable接口。

①定义类实现Runnable接口

②覆盖Runnable接口中的run方法,将线程要运行的代码放在run方法中。

③通过Thread 类建立线程对象

④将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去执行指定对象的run方法,就必须明确该run方法所属的对象。

⑤调用Thread类的start方法开启线程,并调用Runnable接口子类的run方法。

实现方式和继承方式的区别:

继承Thread:线程代码存放在Thread子类的方法中。

实现Runnable:线程代码存放在Runnable接口子类的run方法中。

实现方式的好处:避免了单继承的局限性,在定义线程时,建议使用实现的方式。

多线程运行时产生安全问题的原因?

当多条语句在操作同一个线程的共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

解决方法:

对多条操作共享数据的语句,只能让一个线程执行完,在执行过程中,其他线程不可以参与执行。

java对于多线程的安全问题提供了专业的解决方式:

同步代码块:

synchronized(对象)

{

需要被同步的代码块

}

对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获得了cpu的执行权,也进不去,因为没有获取锁。

同步的前提:

①必须要有两个或者两个以上的线程。

②必须是多个线程使用同一个锁。

③必须保证线程中只有一个线程在运行。

同步的利弊:

利:解决了多线程的安全问题

弊:多个线程需要判断锁,较为消耗资源。

如何找问题?

①明确哪些代码是多线程要运行的代码。

②明确共享资源。

③明确多线程要运行代码中哪些语句是操作共享数据的。

同步函数用的是哪一个锁呢?

函数需要被对象调用,那么函数都有一个所属的对象引用,就是this,所以同步函数所用的锁就是this。

如果同步函数被静态修饰后,使用的锁是什么呢?

因为静态方法中不可以定义this,当静态进内存时,内存中还没有本类的对象,但是一定有该类对应的字节码文件对象:类名.class,该对象的类型是class,故静态的同步方法使用的锁是该方法所在类的字节码文件对象。类名.class

线程间的通讯:其实就是多个线程在操作同一个资源,但操作的动作不同。

wait(),notify(),notifyAll()

这三个方法都要使用的同步中,因为他们的线程操作必须要有监视器()锁,而只有同步中才有锁。

为什么这些操作线程的方法要定义在Object类中呢?

因为这些方法在操作同步中线程时都必须要标识它门所操作的锁,只有同一个锁上的被等得线程,才可以被同一个锁上的notify唤醒。不可以对不同锁上中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,

所以可以被人以对象调用的方法定义在object类中。

对于多个生产者和消费者,为什么要定义while判断标记?

原因:让被唤醒的线程再一次判断标记。

为什么要定义notifyAll()?

因为要唤醒对方的线程,如果只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。

JDK1.5中提供了多线程开发解决方案:

将同步synchronized替换成现实Lock操作。将Object中的wait,notify,notifyAll,替换成condition对象,该对象可以Lock锁进行获取。

如何停止线程?

因为stop方法已经过时,故只有run方法结束才能停止线程,开启多线程运行时,运行代码通常是循环结构,故只要控制住循环就可以让run方法结束,也就是线程结束。

特殊情况:

当线程处于冻结状态,就不会读取到标记,那么线程就不会结束,当没有指定的方法让冻结的线程恢复到运行状态时,这时需要对冻结进行消除。

join方法:当线程A执行到了B线程的.join()方法时,A就会等待B线程都执行完,A才会执行。join可以用来临时加入线程执行。

第五章:网络编程技术

不同机器之间网络通讯的条件:

①找到对方IP

②数据要发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识,为了方便称呼这个数字,叫做端口:逻辑端口。

③定义通信规则,这个通讯规则称为协议。国际组织定义了通用协议Tcp LIP

特殊的IP地址:127.0.0.1

称为本地回环地址,如果本机上没有配置任何IP地址的情况下,本机默认的地址就是127.0.0.1可以用它来测试网卡

在DOS环境下运行:ping127.0.0.1

网络参考模型

OSI参考模型 TCP|

应用层

应用层(http,ftp协议)

表示层

会话层

传输层 传输层(TCP、udp协议)

网络层 网际层(IP协议)

数据链接层 主机至网络层

物理层

网络通讯要素:

1、IP地址:InetAddress

①网络中设备的标识

②不易记忆,可用主机名

③本地回环地址:127.0.0.1 主机名:localhost

2、端口号

①用于标识进程的逻辑地址,不同的进程的标识

②有效端口:0-65535,其中0-1024系统使用或保留端口

3、传输协议

①通讯规则

②常见协议:TCP、UDP

TCP和UDP的区别

UDP:

①将数据及源和目的封装到数据包中,不需要建立连接

②每个数据报的大小限制在64k内

③因无连接,所以是不可靠协议

④不需要建立连接,速度快。

事例:QQ、飞信、凌波多媒体等都是使用的UDP协议

TCP:

①建立连接,形成传输数据的通道

②在建立连接中进行大量数据传输

③通过三次握手完成连接,是可靠协议

④必须建立连接,故效率会稍低。

事例:打电话

Socket:

①Socket就是为网络服务提供的一种机制

②通信的两端都要有Socket

③网络通信其实就是Socket间的通信

④数据在两个Socket间进行IO传输

需求:通过UDP传输方式,将一段文字数据发送出去

思路:(相当于定义一个udp发送端)

①建立udpsocket服务

②提供数据,并将数据封装到数据包中

③通过socket服务的发送功能,将数据包发送出去

④关闭资源

关键代码:

1:创建udp服务(通过DatagramSocket对象)

DatagramSocket ds=new DatagramSocket();

2:确定数据,并封装成数据包

byte [] buf=”zhangudp”.getBytes();

DatagramPacket dp=new DatagramPacket(buf,buf.length,InetAddress.getByName(192.168.1.254),1000)

3:通过socket服务,将已有的数据包发送出去(通过send方法)

ds.send(dp);

4:关闭资源

ds.close();

需求:定义一个应用程序,用于接收udp协议传输的数据并处理。

思路:(相当于定义一个udp的接收端)

①定义udpsocket服务,通常会监听一个端口,其实就是给这个接收网络应用程序定义数字标识,方便于明确哪些数据过来该应用程序可以处理。

②定义一个数据包,因为要存储接收到的字节数据,因为数据包中有更多功能可以提取字节数据中的不同数据信息。

③通过socket服务的receive方法将收到的数据存入已经定义好的数据报包中。

④通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上

⑤关闭资源

关键代码:

1:创建UDP socket服务的站点

DatagramSocket ds=new DatagramSocket(10000);

2:定义数据包,用于存储数据

byte[] buf=new byte[1024];

DatagramPacket dp=new DatagramPacket(buf,buf.length);

3:通过服务的receive方法,将收到的数据存入数据报包中

ds.receive(dp);

4:通过数据报包中的方法获取其中的数据

String ip=dp.getAddress().getHostAddress();

int port=dp.getPort();

String data=new String(dp.getdata(),0,dp.getLength);

5:关闭资源

ds.close();

需求:编写一个聊天程序

有收数据的部分和发数据的部分,这两部分需要同时执行,那就需要用到多线程技术,一个线程控制爱收,一个线程控制发。因为收和发的动作是不一致的,所以需要定义两个run方法,而且这两个方法要封装到不同的类中。

TCP传输:

①Socket 和serverSocket

②建立客户端和服务器端

③建立连接后,通过socket中的IO流进行数据的传输

④关闭socket

同样客户端与服务器端是两个独立的应用程序。

演示TCP传输:

客户端:通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机,因为TCP是面向连接的,所以在建立socket服务时,就要有服务器的存在,并连接成功。形成通路后,在该通道进行数据的传输。

需求:给服务器端发送一个文本数据

关键代码:

1:创建客户端的socket服务,并指定目的主机和端口号

Socket s=new Socket(“192.168.1.254”,10003);

2:为了发送数据,应该获取socket流中的输出流

OutputStream out=s.getOutputStream();

3:确定发送数据,并发送

out.write(“zhang”.getBytes());

4:关闭资源

s.close();

需求:定义端点接收数据并打印在控制台上

服务器端:

1:建立服务端的serverSocket服务,并监听一个 端口

2:获取连接过来的客户端对象,通过serverSocket的accept方法,没有连接就会等,所以这个方法时阻塞式的。

3:客户端如果发过来数据,那么服务端使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据,并打印在控制台上。

4:关闭服务端(可选,因为有时候服务器端需要一直开启)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: