1、面向对象
Java中的对象主要是用来封装事物。
一般事物都会具备:属性和行为(功能)
使用Java描述事物的时候,事物的属性使用类中的成员变量,事物的行为使用类中的方法来描述
事物名称
class Person{
//定义类中的成员变量,就是事物的属性
private String name;
private int age;
/*
如果人这类事物在java中需要创建对象,并且在创建对象的时候,就需要明确具体的数据
那么可以在描述这个事物的时候,书写这个事物的构造方法
构造方法的定义格式:
修饰符 类名()
{
}
*/
public Person( String name , int age ){
this.name = name;
this.age = age;
}
/*
构造方法的功能:
是在创建对象的时候给当前的这个对象进行初始化。
当在new对象的时候,在对象中的成员变量默认初始化结束之后,会自动的调用
与之对应的构造方法
new Person(); 这时会先在堆内存中创建一个Person对象,会把类中的
非静态的成员变量存放在当前的空间中,开始给这些非静态的成员变量默认初始化,
当默认初始化结束之后,就会调用Person类中的无参数的构造方法。
在类的构造方法中有三句隐式的语句:
public Person()
{
//1、super();
//2、给当前对象中的成员变量显示初始化
//3、构造代码块执行
4、构造方法中的代码执行
}
当这个构造方法执行完之后,这个对象就创建完成。
*/
/*
类中可以定义成员方法:
成员方法主要用于描述事物的功能或者行为
*/
public void say(){
System.out.prinln(this.name+"......"+this.age);
}
/*
当一个类中的所有的成员变量都是私有的时候,需要对外提供访问本类中私有的成员变量的方法
一个成员变量需要提供2个方法,get和set方法
内省
*/
//对外提供给本类成员变量name赋值的方法
public void setName( String name ){
this.name = name;
}
//对外提供访问本类成员变量name的方法
public String getName(){
return this.name;
}
}
======================================
面向对象的三大特征:
1、封装:
隐藏具体的实现细节,对外提供访问内部细节的功能。
2、继承:
继承让类与类之间产生了关系。继承需要关键字 extends
当子类继承父类之后,那么子类就可以使用父类中定义的内容。父类中私有的成员子类也是无法访问。
当子类和父类属于同一个事物体系的时候,子类才去继承父类。
继承中成员变量的变化:
当创建子类对象,在子类对象的空间中有会父类的成员变量
继承中的成员方法的变化:
当子父类中出现一模一样的成员方法的时候,把这种现象称为方法的复写(覆盖、重写)
方法还有一个特性:方法的重载。在本类中出现了同名的方法,但是方法的参数列表不同。
什么时候使用方法的复写:
当父类中的功能无法满足子类的需求时,子类可以把父类中的这个方法体重写。
建立适合子类自己的方法体。
方法重写的注意事项:
1、子类在复写父类中的方法时,要求方法名,参数,返回值类型必须相同
2、子类复写父类中的方法时,要求子类的权限要大于或者等于父类的访问权限
访问权限:public protected default private
注意:私有不参数复写。
3、静态的方法只能被静态方法复写。
复写时可以修改的访问权限,不能修改访问方式。
4、子类在复写父类中的方法时,如果父类中的方法没有声明异常,子类在复写的时候,子类也不能声明异常。
如果子类复写了父类中方法,可以子类方法中出现了编译时期的异常,这时子类不能在方法上声明,
只能在方法中捕获。try-catch
5、子类复写父类的方法时,如果父类中的方法声明了异常,子类复写时,只能声明和父类相同的异常,
或者父类声明异常中的子类异常。
6、如果父类中声明了多个异常,子类在复写的时候,只能声明父类这些异常中的子集。
继承中构造方法的变化:
在创建子类对象的时候会先调用父类的构造方法,子类创建对象调用父类的构造方法目的只有一个,
就是给 子类空间中存放的父类成员变量进行初始化动作。
当父类中没有空参数的构造方法时,子类一定要手动显示指明调用父类的那个有参数的构造方法。
3、多态:一个事物的多种表现形态 一个数 10101010101
多态的前提:继承或者实现
什么情况下才会形成多态:
当父类的引用或者接口的引用指向了自己的子类或者实现类对象时,这种现象就称为对象的多态。
Person p = new Person();
Student s = new Student();
Person p = new Student(); 把这种现象成为类型的提升,向上转型
一旦发生了多态,在使用上就有变化:这时只能调用父类中的共性内容的。
细节:
1、成员方法
编译时期看引用所属的类或接口中有没有这个方法,有编译通过,没有编译失败
运行时期看具体的对象,也就是真正在运行的对象属于类中的方法
List list = new ArrayList(); 多态
list.add();
2、成员变量
编译时期看引用所属类中有没有这个变量,有编译通过,没有编译失败
运行时期仍然看引用属于类中有没有这个变量,有就运行这个变量
3、静态方法
编译和运行都看引用所属类。静态的方法和类绑定在一起。静态的方法运行是不需要对象的
========================
抽象类,接口:
把子类的共性内容进行抽取,在抽取到父类中的时候,发现具体的行为体没有办法描述清楚,就使用抽象方法来描述这个功能。
由于在一个类中出现了抽象方法,那么这个类就称为了抽象类。
抽象类的特点:
抽象类需要使用abstract关键字类修饰
abstract class Demo{
权限修饰符 abstract 返回值类型 方法名(参数列表);
}
抽象类的细节:
1、抽象类也是类,它也是用来描述事物的。它永远都处于事物的顶层,
也就是说抽象类一定是父类。
2、抽象类中一定有构造方法。虽然抽象类不能创建对象,但是抽象类中的构造方法
是给子类在创建对象使用的。
3、抽象类可以没有抽象方法,这样的抽象类称为适配器类。
它存在的目的是从接口过度到真实实现类之间的桥梁。
4、抽象关键字不能和那些关键字共存呢?
static
final
private
接口:
接口的功能:
1、继承让类和类之间有了关系,并且子类和父类中都是在描述这个事物体系中应该具备的基本功能
接口的出现可以给某个事物增加额外的功能。
2、可以定义规则,让双方都去遵守这个规则,一方使用规则,一方实现规则。
笔记本的USB
面向接口编程。
接口的定义:
class 是定义类
interface 定义接口
不管是类还是接口编译完之后生成的都是class文件。
============================
this: 代表当前调用这个功能的那个对象
super: 父类的那个空间,通过它可以在子类中访问父类中的成员
static: 主要用于修饰类中的成员变量和成员方法,被静态修饰的成员称为类的成员。
没有被静态修饰的成员,称为对象的成员
被静态修饰的成员,可以直接被类名调用。
被静态修饰的成员,不能去调用非静态的成员。但非静态的成员可以调用静态的成员。
final: 它可以修饰类,成员,具备变量
被它修饰的类为最终类,不能被继承
被它的方法为最终方法,子类无法复写
被它修饰的变量为常量,在整个程序值是固定不变的,因此这个变量名一般都会全部大写
extends: 子类继承父类,接口和接口之间也可以继承
implements: 实现类去实现接口。
=============================
内部类:
把一个类写在另外一个类的内部,这个内部包括类的成员位置和局部位置。
访问内部类的方式:
如果在成员位置上,没有被私有,没有被静态,那么这个成员位置上的内部类属于
外部类对象的成员。那么就可以创建外部类对象,然后在创建内部类对象, 就可以访问内部类中的成员。
如果在成员为上被静态,但没有私有就可以使用外部类名直接访问这个内部类。然后创建内部类对象,访问
内部类中的成员。
成员位置上的内部类被静态,内部类中的成员也被静态。可以直接外部类.内部类.内部类成员
注意:当内部类在外部类成员位置上,被私有了。在外部类以外的地方是无法访问的,
那么只能在外部类的内部访问,可以是外部类中的其他方法中访问。
当内部类在外部类的局部位置上时,这个内部类只能在这个局部范围内被访问,出了这个局部访问,就无法访问了。
匿名内部类:
new 父类或接口名(){
复写父类或实现接口方法
};
匿名内部类都是当前这个类或者接口的子类对象。
===========================
包:
创建包 package 包名 包名一般都是公司的域名倒着写。
cn.itcast.sh.servlet
cn.itcast.sh.service
cn.itcast.sh.dao
cn.itcast.sh.domain 这里面存放的都是简单的Java类,
这些类简单到只有get或者set方法,这样的类称为javabean 或者pojo
导包:import 包名 类名;
静态导入: import static 包名 类名 方法名(成员变量名) ;
===========================
异常:
异常分类:
Exception:异常,异常我们都会给针对性的处理
error:错误
Exception分类:
编译时期异常:Exception
编译时期的异常需要在方法上声明,或者在方法中捕获
这类异常是在编译的时候,编译会检测的异常。如果不对这类异常处理,那么就无法编译通过。
运行时期异常:RuntimeException
这类异常编译时期编译器不会检测它,如果在运行的过程中真的发生了异常,程序就会停止。
异常的处理方案:
1、声明:在方法上使用throws关键字声明当前这个方法中的异常
声明的目的是让调用者知道在这个方法中有异常,需要调用者提前给处理方案。
2、捕获:在方法中有异常发生,这些异常不能被在方法上声明,或者这些异常不能被抛出去。
只能在方法中使用try{}catch(){}捕获
自定义异常:
当我们在程序需要定义一些可读性更强异常时,可以自定去定义异常,自己定义的异常,
只需要继承Exception或者继承RuntimeException,然后在自己的异常中书写两个构造方法即可
2、多线程
创建线程方式:
1、继承Thread类
a、子类继承Thread
b、子类复写run方法,在run方法中书写具体线程要执行的任务代码
c、创建子类对象,调用start方法开启线程
2、实现Runnable接口
a、定义类实现Runnable接口
b、实现Runnable接口中的run方法
c、创建实现类对象
d、创建Thread类对象,并把实现类对象作为从参数传递给Thread对象
e、开启线程
线程的安全问题:
安全问题发生的原因是:多个线程操作了共享数据,并且操作共享数据的语句不止一条。
并且这些语句对共享的数据有操作。
解决:加入同步代码块 synchronized(对象/锁){}
解决的本质是当有线程要执行同步代码块中的代码时,先要获取锁,如果已经有线程
把锁拿走了,那么当前的线程就会在同步代码块的外面处理临时等待状态。等在同步代码块中的
线程执行完之后,把锁释放了,其他线程才有可能获取到锁。
注意:如果加入了同步,线程安全问题依然发生了。
1、确认同步加入的位置是否正确
2、同步使用的锁是否是相同的。
死锁:
最常见的表现锁的嵌套
t1线程 先获取A 锁 再获取B锁
t2线程 先获取B 锁 再获取A锁
多生产多消费:
有多个线程,它们都在操作共享的资源,但是操作共享资源的方式不一样。
如果要让多生产多消费和谐,就需要在它们之间加入等待和唤醒机制(线程间通信)。
jdk5中增加的锁:
Lock 它把隐式的获取和释放锁变成显示有程序员手动的来控制。
Condition监视器对象。
一个Lock下面可以有多个监视器对象,每个监视器都可以去监视不同的线程。
3、集合:
集合的出现主要用于解决存储对象的问题。并且可以存储具备一定对应关系的对象。
Collection:单列集合,即是在其中存放的对象都是没有对应关系的数据
增加:add
删除:remove
遍历 :iterator迭代器
迭代器遍历集合的方式:
先看集合中有没有元素,有才会取出这个元素。如果没有就遍历结束
Collection coll = new ArrayList();
coll.add(1);
coll.add("abc");
coll.add('A');
coll.add(new Object());
for(Iterator it = coll.iterator();it.hasNext();)
{
Object obj = it.next();
}
Collection的子接口:
List:可以存放重复元素,还有索引。它还有自己的迭代器ListIterator
ArrayList:底层使用的可变数组结构,有索引,查询快,增删慢,不同步。
它中的定义的特有功能都是围绕索引而设计的
LinkedList:底层使用的链表结果,有头有尾,可以在头部和尾部进行操作,增删快,查询慢。
它中的特有功能都是围绕头和尾设计。它可以模拟队列或者堆栈数据结构。
Set : 不重复
HashSet:底层使用的哈希表,不重复,无序,不同步
它保证对象唯一方式:通过对象的hashCode方法计算哈希值,如果出现对象的哈希值相同,
然后调用对象的equals方法再进行比较看是不是同一个,如果equals方法返回true就认为是
同一个,当前这个对象不会被保存。否则会存储。
LinkedHashSet:底层使用的链表+哈希表结构。它重点是可以保证存取有序。
TreeSet:底层使用二叉树结构,它的特点是可以给存入其中的对象进行排序。并且可以保证对象唯一。
它的排序要求:
如果给TreeSet集合中存储对象,要求这个对象必须具备比较功能。要使对象具备比较功能
那么这个对象所属的类必须实现Comparable接口,并且实现其中的compareTo方法。
当在把这样的对象给TreeSet集合中存放的时候,就会去调用复写的compareTo方法。
根据返回的大于零或者小于零决定当前这个对象到底给二叉树的左边存放还是右边存放。
当这个方法返回的是零,就认为对象相同的,不存。
如果对象不具备比较功能,或者对象具备的比较功能不是我们所需要的,这时可以给TreeSet集合传递一个
比较器Comparetor。在这个比较器中有2个方法,equals和compare方法。我们需要去实现
compare方法,在这个方法对两个对象进行比较。
Map:存放的key-value这样的键值对关系数据。
map集合是key不重复。一个key对应一个value值。
因此我们可以根据map的key获取map集合中的value值。
map集合的方法:
添加:put(key,value);
删除:remove(key);
获取所有的key,或得到一个set集合。keySet
获取key-value键值对应关系,把这个关系封装成了一个对象Map.Entry
entrySet的到一个Entry集合
Map<String,String> map = new HashMap<String,String>();
map.put("aaa","bbbb");
map.put("ccc","ddd");
map.put("eee","ffff");
map.put("qqq","zzzz");
map集合没有自己的特有遍历方式,可以先把map集合的key取出来变成一个Set集合,然后通过遍历Set集合
取出Set集合中的每个key值,然后再通过key找到map中的value
Set<String> keySet = map.keySet();
for( Iterator<String> it = keySet.iterator(); it.hasNext(); ){
String key = it.next();
String value = map.get(key);
}
使用map集合中的entrySet方法得到一个所有key和value组合成的对象
Set<Entry<String,String>> entrySet = map.entrySet();
for( Iterator<Entry<String,String>> it = entryeSet.iterator();it.hasNext(); ){
Entry<String,String> entry = it.next();
String key = entry.getKey();
String value = entry.getValue();
}
获取map集合中的所有value值 values()
Map的子类:
HashMap:
LinkHashMap:
TreeMap:
元老级别的集合:
Vector:它被ArrayList代替
Hashtable:被HashMap代替
它们两个都是线程同步的。什么的都慢
Hashtable它的下面有个子类Properties,这个类可以和IO集合,主要用于操作配置文件。
properties配置文件:
它的特点是其中只能配置key=value这样的关系数据。
如果要配置更加复杂的数据关系不能使用properties配置文件。
就需要后期学习的xml配置文件来搞定
xml文件中主要配置一些数据之间更加复杂的关系:
<省 name="陕西省">
<市 name="西安">
<>
</市>
<市 name="咸阳市">
<>
</市>
<市 name="宝鸡市">
<>
</市>
<市 name="延安市">
<>
</市>
</省>
泛型:
格式:<数据类型>
List<String> list = new ArrayList<String>();
泛型的出现目的是把运行时期的问题提前到了编译时期。在编译的时候可以让编译器去检测容器中类型是否匹配,
如果不一致,直接编译报错,这样就能够保证在编译通过的容器中存放的对象类型是一致的。
泛型可以定义类上,也可以定在方法上:
class Demo<T>{
T t ;
public void method(T x){
}
}
在类上定义的泛型,在创建对象的时候可以明确具体的类型,在创建对象的时候指定的是什么类型,类上的泛型就是什么类型
Demo<Integer> d = new Demo<Integer>();
d.t = 100;
d.t ="123"; 报错
d.method(123);
d.method("abc"); 报错
泛型定义在方法上:定义的位置是在返回值的前面
class Demo<W>{
public <Q> void func( Q x){
}
//静态的方法无法使用类上的泛型
public static <B> void method( B y){
}
}
泛型的限定:
限定上限:
限定下限
容器<? extends Person> 这个容器中只能存放Person和Person的子类
引用<? super 对象的类型> = 对象
x<? super Student>
4、IO流
I:input 输入
O:output 输出
以内存为中心研究到底是输入还是输出。
数据进内存称为输入,读操作。数据从内存中出去,称为写,输出操作。
数据进入内存,数据的来源可以硬盘、键盘、网络等设置
InputStrean 字节输入流
Reader 字符输入流
数据出内存,可以把数据写到硬盘,网络,显示器等设备
OutputStrean 字节输出流
Writer 字符输出流
要把文件中的数据读到内存中,需要使用的流是:
如果文件是字符文件:FileReader
如果是字节文件:FileInputStream
读取数据有模版代码:
字节流:
FileInputStream fis = new FileInputStream(file);
//单字节读取 read 它读取的数据会存放到ch空间中
int ch;
while( (ch = fis.read())!=-1 ){
//在这里就可以处理读取到的字节数据 ch
}
//一次读取多个字节 read(buf) 一次从底层读取多个字节数据,存放到buf数组,然后会返回去读的字节个数
byte[] buf = new byte[1024]; 用于存储一次读取的多个字节数据
int len = 0; 用于存储一次到底读取了多少字节数据到buf中
while( (len = fis.read(buf))!=-1 ){
//在这里就可以处理数据,数据在buf中,共计存放了len个字节数据
}
fis.close();
字符流:
FileReader fr = new FileReader(file);
//一次读取一个字符 read 它会返回读取到的字符数据
int ch;
while( (ch=fr.read())!=-1 ){
//字符就在ch中,ch中存放的是字符的编码值
}
//一次读取多个字符数据 read(cbuf) 一次可以从底层读取多个字符数据,并且返回读取的字符个数
char[] buf = new char[1024];
int len = 0;
while( (len = read(buf))!=-1 ){
//字符数据在buf数组中,数组中共计有len个字符
}
fr.close();
字符或字节输出流:
以文件为例:
字节:FileOutputStream
字符:FileWriter 注意:字符流有自己的缓冲区,在写出的时候一定要刷新。
=====================================================
如果要把字节数据转成字符数据:
转换流:InputStreamReader,它可以把字节数据转成字符数据,它是外围设备上的字节转成字符到了内存中,并且可以指定编码表
如果要把字符数据转成字节数据:
转换流:OutputStreamWriter,它 是把内存中的字符数据转成字节数据,最后写出去。它也可以指定编码表
编码表的问题:
把生活中的数据和计算机中的数据进行对应,生活中的就是字符数据,计算机中的数据是字节数据
生活中的字符 计算机中的数据 二进制
'A' 65 01000001
'B' 66 01000010
.....
这个编码表就是ASCII
欧洲:拉丁码表 ISO8859-1。这个码表包含ASCII
ASCII和ISO8859-1它们都采用一个字节表示一个字符数据。
中文码表:
gb2312 升级后 GBK 现在又有了新的编码表 gb18030
JDK内置的unicode编码表,这个码表可以识别中文。
后期unicode码表升级了。升级成了utf-8
编码和解码:
编码:把我们生活中的数据变成计算机能够识别的数据。就是把看懂的变成看不懂的。
字符--->字节
解码:把看不懂的变成能够看懂的。
字节--->字符
由于了编码和解码,就会出现乱码:
gbk编码 gbk解码
字符--------------------> 字节 -------------------> 字符
|
|
| utf-8解码
|---------------------> 字符数据,乱码
|
|
|
使用utf-8编码 |
字节 <---------------------------|
|
| gbk解码
|-------------------> 不一定能够得到正确的数据
============================================
缓冲区:
不管是字节流还是字符流都缓冲区
缓冲区的存在是为了提高数据读写效率。
开发时用到字符流缓冲区比较多,在字符流的缓冲区中有自己的特有方法,可以把字符按照读或者写
BufferedReader
readLine 一次读取一行数据
BufferedWriter
newLine 写出一个空行
要读取键盘录入:
键盘录入的数据只能通过字节流来读取
System.in 它会得到一个InputStream流对象
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = bufr.readLine();
流的操作规律:
1、明确数据源:
数据在那里:
读取数据 InputStream 或者 Reader
写出数据 OutputStream 或者Writer
2、明确数据类型:
字节:读 InputStream 字符读 Reader
字节:写 OutputStream 字符写 Writer
3、明确操作的目的地类型
文件 在流的前面加File
网络: 网络流
4、要高效吗?
使用缓冲区
5、要转换吗?
转换流
======================================
功能流:
注意这些功能流的具体功能,它们到底能完成什么功能。
打印流 PrintWriter PrintStream 打印流可以自己刷新
序列化和反序列化:ObjectInputStream
ObjectOutputStream
注意要把对象序列化,只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。
管道流:PipedInputStream
PipedOutputStream
PipedReader
PipedWriter
输入流的合并流 SequenceInputStream 序列流
可以把多个输入流合并成一个输入流
随机访问流:RandomAccessFile
5、网络编程
主要是用于网络的数据传输。网络的数据传输需要基于传输层的协议。
传输层的协议:UDP 和 TCP
UDP:不安全,无连接,传输速度快,但数据有限
TCP:安全,有连接,必须经过三次握手,保证建立连接通道。可以传输大数据量的数据。
网络三要素:
ip
端口
协议
如果要使用UDP通信,UDP通信类似于对讲机
DatagramSocket 建立发送端或者接收端的对象。
使用DatagramPacket对数据进行封包,然后才发送数据
如果使用TCP通信,类似于打电话
TCP分为客户端和服务端
客户端:Socket
服务端:ServerSocket
在服务端需要获取到底是那个客户端连接到服务端上,然后就可以获取客户端发送给服务端的数据。
双方就可以进行通信。后期大家全部使用TCP在通信。