面向对象
面向对象是一种思想,能让复杂的问题简单化,让我们角色从执行者变成指挥者,不要知道过程,只要知道结果。(一切皆对象。)
描述一个事物,其实就是在描述事物的属性和行为。
对象的特点在于封装数据,数据包含着属性和行为。
我们用类来描述一个事物,然后引用类来建立一个对象。
也可以在本类建立一个主函数来中创建本类对象,这个叫在本类中创建本类对象。
一般一个程序可以由很多个类组成,也可以有多个主函数,但一般有一个主函数就可以。
格式:引用类名 对象名=new 构造函数名(参数);
例:class DuiXiang{}
则建立对象为:DuiXiang mingZi=new DuiXiang();
如果要实现功能,则格式为:
mingZi.变量或函数名();
(string是java中的字符串。 String类是不可变的,对String类的任何改变,都是返回一个新的String类对象。 String 对象是 System.Char 对象的有序集合,用于表示字符串。String 对象的值是该有序集合的内容,并且该值是不可变的。)
面向对象主要的部分:(类和对象的关系,封装(机箱故事),继承,多态,构造函数,this,static,内部类,抽象类,接口)
面向对象之封装
函数是最小的封装体。类也是一个封装体。
private:私有的,权限修饰符,用来修饰类中的成员(成员变量 、成员函数),其只在本类中有效。
每个成员变量通常都会对应两个访问方式:一个设置,一个获取。
注意:私有仅是封装的一种表现形式。
因为语句都必须在函数内,所以,私有化后,再提供访问方式,就可以在访问方式的函数内实现变量控制。这样就提高代码的健壮性。
一般进行封装,要对类中的大部分属性都隐藏,最低权限是private。
类里有一个setXxx函数(一般返回值是void,直接获取,要带参数)和getXxx函数(有返回值类型,但一般没参数,getXxx之后一般用一个变量来接收:string x=p.getXxx
),那代表一定有一个私有化属性。
成员变量都有初始化值, 局部变量可以没有。
举例:
class XueSheng
{
private String name;
public void setName(String name)//这个函数方便以后赋值
{
this.name=name;
}
public String getName()//这个函数是方便以后有需要调用的,比如以后要打印name的值
{
return name;
}
构造函数:
函数名与类名一致,不用返回值类型,不可以用return,主要用来初始化对象。
对象一建立,就默认调用其构造函数。一个对象一建立,就有其默认的属性和行为。(如一个人刚出生就会呼吸,哭...)。
如果我们没有指认,那么类建立就会默认建一个构造函数(类名(){}),不然对象无法初始化,也就无法建立。(注意:只要我们有指认,那么就不会默认建构造函数)
其与普通函数的区别除了写法上之外:
1,构造函数只在对象建立时运行一次,不再运行了,而普通函数可以调用多次,另外,构造函数是用来初始化对象的,而一般方法是用来添加对象具备的功能。
何时我们要手动添加构造函数:当我们分析事物时,该事物存在一些特性或行为,那么我们就给其定义一个构造函数,如果有变量参与运算,那么我们就给构造函数定义一个参数。
构造函数写法举例:
class Person
{
Person(String name,int age)
//注意:在新建一个对象时要加上参数进行区分,因为一个类可以放很多个构造函数。
//比如:Person p =new Person("fada",20)
{
this.name = name;
this.age = age;
}
构造代码块:
1,所有对象在建立时都先执行构造代码块初始化,再执行构造函数初始化。
2,作用:当所有对象有共性时,那么就可以定义一个构造代码块(例如:所有小孩先出来就是哭,然后才有其它的属性)
构造代码块的写法(就是在类里面用一个大括号)举例:
class Person
{
{
System.out.print("fada")
//这样一写,那么以后每次建立一个构造函数时便先初始化这个构造代码块
}
}
this关键字的用法
This在类中就是三个代表:代表对象的成员变量,在函数中代表对象调用函数,代表类中的构造函数。
格式:this.变量=变量;
this是用来区分局部变量和成员变量同名时的关键字,因为如果在构造函数里比如(name=name),那么其是赋值给他本身,而不是赋值给类里面的name。
何是用this?当定义类中的函数时,需要调用该函数的对象时,这个时候就用this来表示这个对象。
但凡本类功能内部使用到了本类对象,用this表示。
看到this就是有代表对象,代表哪个对象就看其所在功能被哪个对象调用。
这样就知道谁在参与运算。
例:
class Person
{
private String name;
Person(String name)
{
this.name = name;//this.name=p1.name;
}
}
class PersonDemo3
{
public static void main(String[] args)
{
Person p1 = new Person("fada");
this的应用之定义类中函数的调用:当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。
但凡本类功能内部使用了了本类对象,都用this表示。
例:
class Person
{
private String name;
Person(String name)
{
this.name = name;//this.name=p1.name;
this.fada();//其实就==p1.fada();
//这里写这p1是因为p1这个对象在调用这个函数。一句话,谁调用它就在代表谁。
}
private int age;
Person(int age)
{
this.age = age;
}
public void fada()
{
System.out.println("fada");
}
/*
需求:给人定义一个用于比较年龄是否相同的功能。也就是是否是同龄人。
*/
public boolean compare(Person p)
{
return this.age==p.age;
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p1 = new Person(20);
Person p2 = new Person(25);
//因为是两个人比,所以要先建立两人的对象
boolean b = p1.compare(p2);
//这里定义了一个布尔型变量去接收p1调用compare函数的值
System.out.println(b);
另一种this用法:格式this(变量)
用于构造函数间的相互调用,而且只能放在构造函数的第一行。
然后先初始化其this调用的构造函数,再初始化本身的构造函数。
其实this(变量);=new 构造函数名(变量);(因为所有构造函数名是一样的,都是重载函数,所以,通过变量来标记构造函数,用this去引用)
例:
class Person
{
private String name;
private int age;
Person()
{
System.out.println("person run");
}
Person(String name)
{
This();
this.name =name;
}
}
Static(静态)关键字:
static是一个修饰符:
三种修饰:修饰类的变量、方法和构造代码块。静态方法只能直接引用和访问静态变量和方法
注意(函数即方法,对象也叫实例)
有时你希望定义一个类成员,使它的使用完全独立于该类的任何对象。通常情况下,类成员必须通过它的类的对象访问,但是可以创建这样一个成员,它能够被它自己使用,而不必引用特定的实例。在成员的声明前面加上关键字static(静态的)就能创建这样的成员。如果一个成员被声明为static,它就能够在它的类的任何对象创建之前被访问,而不必引用任何对象。你可以将方法和变量都声明为static。static 成员的最常见的例子是main( ) 。因为在程序开始执行时必须调用main() ,所以它被声明为static。 声明为static的变量称为静态变量或类变量。可以直接通过类名引用静态变量,也可以通过实例名来引用静态变量,但最好采用前者,因为后者容易混淆静态变量和一般变量。静态变量是跟类相关联的,类的所有实例共同拥有一个静态变量。 关键点:静态变量与实例变量的区别:静态变量也叫类变量,为所有对象所共有,所以一个对象的变量值改变,那么所有对象的值一起发生改变。
而实例变量则是各自独立的不受影响。
声明为static的方法称为静态方法或类方法。静态方法可以直接调用静态方法,访问静态变量,但是不能直接访问实例变量和实例方法。静态方法中不能使用this关键字,因为静态方法不属于任何一个实例。静态方法不能被子类的静态方法覆盖。
特有属性随着对象存储于堆内存中,而static修饰后的属性,存在于方法区.
什么时候使用静态修饰符?
当对象中出现共享数据时,使用静态修饰。
但对象内的特有数据要定义成非静态存在于堆内存中。
而对于方法时:
当功能内部没有访问到非静态数据时,(即对象的特有数据)
那么可以使用静态修饰。
静态代码块:
用于给类做初始化的。不管有没有对象都执行,只要调用了类里面成员或方法,就会执行,而且优先于主函数,优先执行级别最高。
例:
class JingTai
{
static int age;
static String name;
static//静态代码块初始化类的,所以最先运行。
{
System.out.println("name="+name);
}
//下面是构造代码块,第二个运行
{
System.out.println("age="+age);
}
public static void jing(int x)//被对象调用,第三个运行。
{
x =age+1;//因为age是静态的,所以能调用
System.out.println("x="+x);
}
}
class FaDa
{
public static void main(String[] args)
{
JingTai p =new JingTai();
p.jing(2);
}
}
文档注释:
文档注释有编译工具进行,具体一个要注意的地方是:类名前必须要有public修饰。要编译的方法名必须有public 修饰
javadoc注释标签语法
@author 对类的说明标明开发该类模块的作者
@version 对类的说明标明该类模块的版本
@see 对类、属性、方法的说明 参考转向,也就是相关主题
@param 对方法的说明对方法中某参数的说明
@return 对方法的说明 对方法返回值的说明
@exception 对方法的说明 对方法可能抛出的异常进行说明
命令是:javadoc -d 存放目录名称 -author -version 类名.java
/**
这是一个静态修饰符static的应用说明
@author 李四
@version V1.1
*/
/**
这是一个测试类
*/
public class JingTai
{
/**
空参数构造函数
*/
/**
age 一个整数型变量
*/
public static int age;
/**
name 一个字符串型变量
*/
public static String name;
static
{
System.out.println("name="+name);
}
{
System.out.println("age="+age);
}
/**
打印x的值
@param x 接收一个int型整数
*/
public static void jing(int x)
{
x =age+1;
System.out.println("x="+x);
}
}
注意:main方法作为程序入口,应该在public的类Test中定义
设计模式:
做一个模式,使其能解决一类问题,这样,碰到类似的问题,直接套就可以了。
java中23种设计模式:
举例:
单例设计模式:解决一个类在内存只存在一个对象。不再新生成。
思路:
1,为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象
2,还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
3,为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
步骤:
1,将构造函数私有化。
2,在类中创建一个本类对象。
3,提供一个方法可以获取到该对象。
两种方式:
饿汉式
class Er
{
private Er(){};
private static Er e =new Er();
public static Er getEr()
{
return e;
}
}
懒汉式
class Er
{
private Er(){};
private static Er e =null;
public static Er getEr()
{
if(e==null)
e=new Er();
return e;
}
}
虽然懒汉式可以延迟加载对象,但为代码的简洁性和安全性,日常用的还是饿汉式。
继承
继承就是将两个类的共性描述提取出来,单独进行描述,从而简化代码,提高复用性。
格式:class 子类名 extends 父类名
关健字为extends :继承
判断两个类是否有所属关系,那么我们就继承一下,看父类所具有的内容属性,子类是否全都需要具备。如果是,那么他们所属关系成立,就是继承。
注意:千万不要为了获取其他类的功能,简化代码而继承。
必须是类与类之间有所属关系才可以继承。所属关系 is a。
例如:
class A
{
//void demo1(){}
void demo2(){}
}
class B
{
//void demo1(){}
void demo3(){}
}
我们可以让B继承下A,但发现,A中还具有demo2这个方法是B所不具备的,所以他们俩的继承关系不成立,但我们可以把两个类的共性提取出来,成为一个
class C
{
void demo1(){}
}
那么A和B就能够继承C,这样就实现在代码的简化和复用。
Java语言中:java只支持单继承,不支持多继承。
因为多继承容易带来安全隐患:当多个父类中定义了相同功能,
当功能内容不同时,子类对象不确定要运行哪一个。
但是java保留这种机制。并用另一种体现形式来完成表示。多实现。
java支持多层继承。也就是一个继承体系
如何使用一个继承体系中的功能呢?
想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能。
通过了解共性功能,就可以知道该体系的基本功能。
那么这个体系已经可以基本使用了。
那么在具体调用时,要创建最子类的对象,为什么呢?
一是因为有可能父类不能创建对象,
二是创建子类对象可以使用更多的功能,包括基本的也包括特有的。
简单一句话:查阅父类功能,创建子类对象使用功能。
聚集:has a
事物与事物的另一种关系,比如(类与类,对象与对象)
聚合:例如:一个班里的有很多学生,那么班级和学生就是聚合关系。就是包含。
组合:组合与聚合区别在于,组合各成员是不可分割的,否则会缺少功能,但聚合里的成员可以脱离,对整体没有影响。
子父类出现后,类成员的特点:
类中成员:
1,变量。
2,函数。
3,构造函数。
1,变量
如果子类中出现非私有的同名成员变量时,
子类要访问本类中的变量,用this
子类要访问父类中的同名变量,用super。
super的使用和this的使用几乎一致。
this代表的是本类对象的引用。
super代表的是父类对象的引用。
例如:
class Fu
{
int num = 4;
}
class Zi extends Fu
{
int num = 5;
void show()
{
System.out.println(super.num);
//有super对象调用函数打印结果是4,没有打印结果是5
}
}
子父类中的函数另一个特性:函数的重写(覆盖)
当子类继承父类,沿袭了父类的功能,这时不但可以保留父类的功能定义,还可以重写功能内容。
覆盖要注意的关健点:
1,子类覆盖父类,必须保证子类访问权限大于等于父类权限,才可以覆盖,否则编译失败。
(权限修饰符没设的时候为默认权限,介于public与private之间。)
例如:
class Fu
{
void show()
{
System.out.println("fu show");
}
}
class Zi extends Fu
{
void show()
{
System.out.println("java");
//这里我即可以改变,也可以增加。增加时我们不用重写一遍父类函数的内容,只虽要super.函数名()就可以。
}
2,静态只能覆盖静态。
注意:
重载:只看同名函数的参数列表。
重写:子父类方法要一模一样。
子父类中的构造函数之特点:
子类中的构造函数会默认调用父类中的函数中的默认构造函数,因为子类的构造函数默认第一行有一条隐式的语句 super();
但有几个地方要注意:
例:
class Fu
{
Fu()
注意:如果没有这一个空参数的构造函数,但又有我们指认的非空参数的构造函数,那么我们知道默认那个空参数构造函数也不会生成,这个时候子类就会编译失败。这个时候就不能让子类默认获取super();而要指认super(x)
{
num= 60;
System.out.println("fu run");
}
Fu(int x)
{
System.out.println("fu ...."+x);
}
}
class Zi extends Fu
{
Zi()
{
super(); //不管父类有几个构造函数,只要没有指认,那么就默认这一个super();
//super(4);
System.out.println("zi run");
}
Zi(int x)
{
super();
super(3);
System.out.println("zi..."+x);
}
}
调用构造函数用super();(括号里面是参数),而调用一般函数用super.函数名()
一个构造函数中只能要么有this要么有super,而且必须放在第一行
子类中至少会有一个构造函数会访问父类中的构造函数。
extends Object:java中的顶极父类,也就是任何没标明父类的类的默认父类。
final : “最终”修饰符:
1,可以修饰类,函数,变量。
2,被final修饰的类不可以被继承。作用:为了避免被继承,被子类复写功能。
3,被final修饰的方法不可以被复写。
4,被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,有可以修饰局部变量。
当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读。
而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有字母都大写,如果由多个单词组成,单词间通过_连接。
5,内部类定义在类中的局部位置上是,只能访问该局部被final修饰的局部变量。
例如:
final int x=4;那么x永远等于4,不可以被赋值,就成了常量,之所以不直接定常量,是因为要给数值起个名,增强阅读性,以后也好调用。
抽象
修饰符,abstract:只能修饰类和方法:
特点:
1,修饰的类不能创建对象(实例)。
2,修饰的对象只有功能,没有内容。
3,抽象方法和抽象类都必须被abstract关键字修饰,也就是抽象方法一定在抽象类中,
但抽象类不一定要有抽象方法.
4,抽象类中的抽象方法要被使用,必须由子类复写起所有的抽象方法后,建立子类对象调用。
如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。因为抽象方法和抽象类都必须被abstract关键字修饰(如果只复写了一个,那么子类必然还继承了其它的抽象方法,根据抽象方法必须在抽象类中,则这个类还是个抽象类)。
应用:
1,建立不可创建对象的类。
2,如果子类中需要的方法内容不一样,但又同样都要这么方法,那么就可以创建一个抽象类。
例如:创建一个学校成员,有学生,有老师,都有姓名,年龄,都要上课,但老师上课是教学,学生上课是学习。所以可以这样写:
class XueXiao
{
private String name;
private int age;
XueXiao(String name,int age)
{
this.name = name;
this.age = age;
}
public abstract void work();//子类需要这个功能,但内容不一样,所以用抽象。
}
class Student extends Employee
{
Student(String name,int age)
{
super(name,age);//直接调用父类构造函数。
}
public void work()
{
System.out.println("shangke");
}
}
class LaoShi extends Employee
{
LaoShi(String name,int age)
{
super(name,age);//直接调用父类构造函数。
}
public void work()
{
System.out.println("laoshi");
}
}
模版方法设计模式:
什么是模版方法呢?
在定义功能时,功能的一部分是确定的,但是有一部分是不确定,而确定的部分在使用不确定的部分,
那么这时就将不确定的部分暴露出去。由该类的子类去完成。
abstract class GetTime
{
public final void getTime()//这个类是不需要改变的,所以就用final最终修饰。
{
long start = System.currentTimeMillis();
runcode();
long end = System.currentTimeMillis();
System.out.println("毫秒:"+(end-start));
}
public abstract void runcode();//这个函数主体要设成不确定,这样记算任何函数的运行时间:我只要把要算的函数定义成这个的内容就行了。所以设成抽象类的方法。
}
class SubTime extends GetTime
{
public void runcode()
{
for(int x=0; x<4000; x++)
{
System.out.print(x);
}
}
}
要调用计时这个功能,我们就在要在类中新健一个SubTime的对象。再调用getTime函数的就可以(SubTime类继承了父类,所以有getTime函数。)
接口:
接口:初期理解,可以认为是一个特殊的抽象类
当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。
class用于定义类
关健字:interface 用于定义接口。
接口定义时,格式特点:
1,接口中常见定义:常量,抽象方法。
2,接口中的成员都有固定修饰符,(不写也会自动生成)。
常量:public static final
方法:public abstract
记住:接口中的成员都是public的。
接口:是不可以创建对象的,因为有抽象方法。
需要被子类实现,子类对接口中的抽象方法全都覆盖后,子类才可以实例化。
否则子类是一个抽象类。原因和抽象类一样,有抽象则为抽象类。
接口可以被类多实现,也是对多继承不支持的转换形式。java支持多实现。
1,类可以实现多个接口,用关健字:implements 如:
class Test implements JieKou,JieKou2
2,类继承后,仍可以实现多个接口,如:
class Test extends Demo implements JieKou,JieKou2
3,接口在可以实现多继承,如:
interface Test extends A,B
例:
abstract class Student
{
abstract void study();学生都要学习,所以可以作为父类来给子类继承。
}
interface Smoking
{
void smoke();//抽烟不是所有学生都会,所以,不可以继承,不然所有学生都抽烟了,所以定义为接口,让需要的类实现就行。
}
class ZhangSan extends Student implements Smoking
//继承后再实现,这样就实现了功能的扩展。
{
void study(){}
public void smoke(){}//记得要复写,因为父类是抽象的。
}
class Lisi extends Student
{
void study()
{
System.out.prtintln("study")
}
}
多态:
1,多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。
2,多态的前提
必须是类与类之间有关系。要么继承,要么实现。
通常还有一个前提:存在覆盖。
3,多态的好处
多态的出现大大的提高程序的扩展性。
4,多态的弊端:
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
如何要访问子类特有对象必须强制转换为子类对象。
abstract class Animal
{
abstract void eat();
}
class Cat extends Animal
{
public void eat()
多态还有一个前提:存在覆盖。不然没有意义,只是继承了一个空方法。
{
System.out.println("吃鱼");
}
public void catchMouse()
但是只能使用父类的引用访问父类中的成员。因为父类中并没有catchMouse方法,所以多态无法实现catchMouse
{
System.out.println("抓老鼠");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("吃骨头");
}
}
public static void function(Animal a)//Animal a = new Cat();父类的引用指向了自己的子类对象。父类的引用也可以接收自己的子类对象。
前题:必须是类与类之间有关系。要么继承,要么实现。Animal是动物,Cat是它的子类。
{
a.eat();
}
class DuoTaiDemo
{
public static void main(String[] args)
{
function(new Cat());
function(new Dog());提高了扩展性,不用再去新建一个对象,再引用对象的功能。
}
Object类:
是所有对象的直接后者间接父类.
该类中定义的是所有对象都具备的功能。
如果自定义类中需要的功能是Object含有的,那么没有必要重新定义,只要沿袭父类中的功能,建立自己特有比较内容,覆盖即可。
常用的有比较功能:
public boolean equals(Object obj)
//这里要是Object对象,所以这里相当于:Object obj =new 自定义类()
{
......这里写其自定义的内容,如果这里要有自定义类里的特有功能,那么要强转对象
格式是:自定义类 x = (自定义类)obj;
}
内部类
特点:1,内部类可以直接访问外部类中的成员,包括私有。
(原因:内部类中持有了一个外部类的引用,格式:外部类名.this)
2,外部类要访问内部类,必须建立内部类对象。
3, 当内部类在成员位置上,就可以被成员修饰符所修饰。
比如,private:将内部类在外部类中进行封装。
static:内部类就具备static的特性。
当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问局限。
在外部其他类中,新建立一个对象直接访问static内部类的非静态成员,但对象前
用外类名修饰
new Outer.Inner().function()
在外部其他类中,直接用类名来直接访问static内部类的静态成员,但类名前用外
类修饰
Outer.Inner.function();
注意:当内部类中定义了静态成员,该内部类必须是static的。
当外部类中的静态方法访问内部类时,内部类也必须是static的。
何时应用内部类:
当描述事物时,事物的内部还有事物,该事物用内部类来描述。这个主要体 现一种类的优化思想。而且一般将内部类私有,而通过创建方法给外部调用。
例如:
class Outer
{
private int x = 3;要访问这个是:Outer.this.x
class Inner//内部类
{
int x = 4;;//要访问这个是:this.x
void function()
{
int x = 6;;//如果不修饰则默认访问方法内的成员。
System.out.println("innner :"+Outer.this.x);
}
}
void method()外部类要访问内部类成员就要新建一个内部类对象。
{
Inner in = new Inner();
in.function();
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer out = new Outer();//
out.method();
//直接访问内部类中的成员就必须修饰引用。以下是格式:
外部类名.内部类名 变量名 = 外部类对象.内部类对象;
// Outer.Inner in = new Outer().new Inner();
// in.function();
}
}
当内部类定义在局部时:
1,不可以被成员修饰符修饰
2,可以直接访问外部类中的成员,因为还持有外部类中的引用。
但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量。
例如:
class Outer
{
void method(final int a)
{
final int y = 4;
class Inner
{
void function()
{
System.out.println(y);
}
}
new Inner().function();
}
}
class InnerClassDemo3
{
public static void main(String[] args)
{
Outer out = new Outer();
out.method(7);
out.method(8);
//注意:这里之所以可以,是因为out.method(7);进的是栈内存,用完后就自动消失了,out.method(8);又重新加载了,所以尽管变量被final修饰但是依然可以。
}
}
匿名内部类:
特点:
1,匿名内部类其实就是内部类的简写格式。
2,定义匿名内部类的前提:
内部类必须是继承一个类或者实现接口。
3,匿名内部类的格式: new 父类或者接口(){定义子类的内容}
4,其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。 可以理解为带内容的对象。
5,匿名内部类中定义的方法最好不要超过3个。
应用举例:
interface Inter
{
void method();
}
class Test
{
static Inter function()
{
return new Inter()
{
public void method()
{
System.out.println("method run");
}
};
}
}
class InnerClassTest
{
public static void main(String[] args)
{
Test.function().method();
}
}
异常:
把程序运行中出的问题通过java的类的形式进行描述,并封装成对象,这个对象的体现就是异常.
好处在于:将问题进行封装,从而将正常流程代码和问题处理代码相分离,方便于阅读。
异常有两种,严重的和不严重的。
对于严重的,java通过Error类进行描述。
对于Error一般不编写针对性的代码对其进行处理。
对与非严重的,java通过Exception类进行描述。
对于Exception可以使用针对性的处理方式进行处理。
无论Error或者Exception都具有一些共性内容。
比如:不正常情况的信息,引发原因等。
Throwable
|--Error
|--Exception
2,异常的处理
java 提供了特有的固定语句进行处理。
try
{
需要被检测的代码;
}
catch(异常类 变量)
{
处理异常的代码;(处理方式)
}
finally//这句可以没有。
{
一定会执行的语句;通常用于关闭资源。(不管用了return,还是continue,还是break,其都会运行。
注意:当你在捕获到异常的处理代码里加上:
System.exit(0);
这样的话finally的代码块 是不会执行的。
捕获异常的规则是尽量优先捕获具体的异常。
System.exit(0)和System.exit(1)什么区别? 凡是非零都表示异常退出!0表示正常退出!
)
}
3,对捕获到的异常对象进行常见方法操作。
String getMessage():获取异常信息。
在函数上声明异常。
便于提高安全性,让调用出进行处理。不处理编译失败
对多异常的处理。
1,声明异常时,建议声明更为具体的异常。这样处理的可以更具体。
2,对方声明几个异常,就对应有几个catch块。不要定义多余的catch块。
如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面。
建立在进行catch处理时,catch中一定要定义具体处理方式。
不要简单定义一句 e.printStackTrace(),
也不要简单的就书写一条输出语句。
自定义异常:
必须是自定义类继承Exception,因为异常体系有一个特点就是异常类和异常对象都被抛出,他们要具备可抛性,而这个可抛性是Throwable这个体系中独有特点。
只有这个体系中的类和对象才可以被throws和throw操作,二者区别是:
throws使用在函数上用,用来抛出异常,告诉调用者这里有可能会出现异常。throw使用在函数内用来抛出异常对象让调用者处理。
throws后面跟的异常类。可以跟多个。用逗号隔开。
throw后跟的是异常对象。
Exceptoin中有一个特殊的子类异常RuntimeException: 运行时异常。
如果在函数内容抛出该异常,函数上可以不用声明,编译一样通过。
如果在函数上声明了该异常。调用者可以不用进行处理。编译一样通过;
之所以不用在函数声明,是因为不需要让调用者处理,当该异常发生,希望程序停止,
对代码进行修正。
自定义异常时,如果该异常的发生,无法处理让其再继续进行运算,必须修正代码,就让自定义异常继承RuntimeException,如可以处理,则继承Exception。
对于异常分两种:
1,编译时被检测的异常,那么不要么就catch处理它,不然就必须声名。
2,编译时不被检测的异常(运行时异常。RuntimeException以及其子类)。
应用举例:
人用电脑办公。
比如问题是
电脑蓝屏:重启可以修正错误,继续上班,则继承Exception
停电了:无法修正错误,只能停下来等电力供应恢复。
要对问题进行描述,封装成对象。
class LanPingException extends Exception
{
LanPingException(String message)
{
super(message);
//直接调用父类方法,用来返回此 throwable 的详细消息字符串
}
}
class TingDianException extends RuntimeException
{
TingDianException(String message)
{
super(message);
}
}
class Computer
{
private int state = 2;
public void run()throws LanPingException
{
if(state==2)
throw new LanPingException("蓝屏了");
if(state==3)
throw new TingDianException(" 停电了");
System.out.println("电脑运行");
}
public void reset()
{
state = 1;
System.out.println("电脑重启");
}
}
class Person
{
private String name;
private Computer cmpt;
Person(String name)
{
this.name = name;
cmpt = new Computer();
}
public void prelect()
{
try
{
cmpt.run();
//人要调用run(),所以其接受了异常,其要么抛出,要行么处理。
}
catch (LanPingException e)
{
System.out.println("电脑蓝屏啦");
cmpt.reset();
}
System.out.println("办公");
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Person t = new Person("人");
t.prelect();
}
}
异常的处理要注意的关健点:
1,处理方式有两种:try 或者 throws。
2,调用到抛出异常的功能时,抛出几个,就处理几个。
一个try对应多个catch。
3,多个catch,父类的catch放到最下面。
4,catch内,需要定义针对性的处理方式。不要简单的定义printStackTrace,输出语句。
也不要不写。
当捕获到的异常,本功能处理不了时,可以继续在catch中抛出。
try
{
throw new AException();
}
catch (AException e)
{
throw e;
}
如果该异常处理不了,但并不属于该功能出现的异常。
可以将异常转换后,在抛出和该功能相关的异常。
或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,
当调用者知道。并处理。也可以将捕获异常处理后,转换新的异常。
try
{
throw new AException();
}
catch (AException e)
{
// 对AException处理。
throw new BException();
}
异常在子父类覆盖需要注意的问题:
1,子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
2,如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。
3,如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。
如果子类方法发生了异常。就必须要进行try处理。绝对不能抛。
package包
包的作用:
可以将源文件与class文件分离,有利于安全性。也可以打包并可以直接操作。
两个关健字
package :用来标识class文件其属于哪个包,放于代码的最上面:package 包名(包名全部小写)
import :用于导入,就是要以把包下面的具体的一个类导入,(例如:import.a.b....)以及可以把包下面所有的类导入(例如:import.a.*;)
例:
package bao2;
import bao1.Test;
class Test1
{
public static void main(String[] args)
{
Test a = new Test();
a.show();
System.out.println("fada test1");
}
}
编译格式:
到源文件目录:javac -d e:\myclass Test.java
注意:如果包与包之间有调用,那么用set classpath=要访问的包的目录。
要注意的问题:
类名的全名是:包名.类名,如果要不用,那么就必须import导入功能。
需要设置classpath,告诉jvm去哪里找指定的packa包。
有了包,范围变大,一个包中的类要被访问,必须要有足够大的权限,所以被访问的类要被public修饰。
类公有后,被访问的成员也要公有才可以被访问。
总的来说:包与包之间进行访问,被访问的包中的类以及类中的成员,需要public修饰。
protected(保护权限):不同包中的子类还可以直接访问父类中被protected权限修饰的成员。
包与包之间可以使用的权限只有两种,public protected。
public protected default private
同一个类中 ok ok ok ok
同一个包中 ok ok ok
子类 ok ok
不同包中 ok
jar包
方便携带和使用,在classpath中设制好jar路径就可以了。数据库驱动,SSH等都以jar包形式体现。
编译格式:
jar -cf jar包名 package名1 package名2 package名3 .........
如:jar -cf fada.jar bao1 bao2(注意:要cd到pack包所在目录)
直接运行jar包内的文件,格式:
之前先set classpath=jar包所在目录\jar包名
如:set classpath = e:\myclass\fada.jar
编译格式:java bao2.Test1
继承
何为继承,有何作用
继承就是将两个类的共性描述提取出来,单独进行描述,从而简化代码,提高复用性。
格式:class 子类名 extends 父类名
关健字为extends :继承
判断两个类是否有所属关系,那么我们就继承一下,看父类所具有的内容属性,子类是否全都需要具备。如果是,那么他们所属关系成立,就是继承。
注意:千万不要为了获取其他类的功能,简化代码而继承。
必须是类与类之间有所属关系才可以继承。所属关系 is a。
例如:
class A
{
//void demo1(){}
void demo2(){}
}
class B
{
//void demo1(){}
void demo3(){}
}
我们可以让B继承下A,但发现,A中还具有demo2这个方法是B所不具备的,所以他们俩的继承关系不成立,但我们可以把两个类的共性提取出来,成为一个
class C
{
void demo1(){}
}
那么A和B就能够继承C,这样就实现在代码的简化和复用。
Java语言中:java只支持单继承,不支持多继承。
因为多继承容易带来安全隐患:当多个父类中定义了相同功能,
当功能内容不同时,子类对象不确定要运行哪一个。
但是java保留这种机制。并用另一种体现形式来完成表示。多实现。
java支持多层继承。也就是一个继承体系
如何使用一个继承体系中的功能呢?
想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能。
通过了解共性功能,就可以知道该体系的基本功能。
那么这个体系已经可以基本使用了。
那么在具体调用时,要创建最子类的对象,为什么呢?
一是因为有可能父类不能创建对象,
二是创建子类对象可以使用更多的功能,包括基本的也包括特有的。
简单一句话:查阅父类功能,创建子类对象使用功能。
多线程
线程:每一个进程都有一个执行顺序,这个顺序就是一个独立的控制单元,这个独立的控制单元就是线程,线程在控制着进程的执行。
由一个Thread类来对这类事物进行描述。
自定义一个进程的方法(1):
定义一个类继承Thread类,并且复写Thread类中的run方法,将自定义代码储存在run方法中,让线程运行。最后通过创建一个线程的对象,通过调用线程的start()方法,来启动线程并执行run方法。
例:
class Xian extends Thread
{
public void run()
{
for(int x=0; x<100; x++)
System.out.println("xiancheng run----"+x);
}
}
class Cheng
{
public static void main(String[] args)
{
Xian d = new Xian();//创建一个Thread类或其子类的对象就是建立一个线程。
d.start();//开启线程并执行该线程的run方法。
//d.run();//如果这样写仅仅是对象调用方法。而线程创建了,并没有运行。
}
}
线程都有自己默认的名称,因为其有setName和getName方法。
默认为:Thread-编号 该编号从0开始。
设置线程名称:setName或者构造函数。
例:Test(String name)
{
super(name);//直接调用父类方法
}
创建对象设置名字:Test a1 = new Test("xiancheng1"); Test a1 = new Test("xiancheng2");
通过static Thread currentThread()方法获取当前线程对象,再通调用getName()方法获取线程名称。
如:System.out.println((Thread.currentThread().getName()+" run..."+x);
创建线程的第二种方式:
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性。
所以定义线程尽可能使使用实现方式。
两种方式区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存在接口的子类的run方法。
例:
class Ticket implements Runnable//extends Thread
{
private int tick = 100;
public void run()
{
while(true)//让其循环
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
//创建了一个线程;Thread里默认有一个构造方法用来接收Runnable对象。
这样一来Thread对象里面就有了Ticket对象。就能运行自定义run方法。
Thread t2 = new Thread(t);//创建了一个线程;
Thread t3 = new Thread(t);//创建了一个线程;
Thread t4 = new Thread(t);//创建了一个线程;
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步代码块:
作用:用于解决多线程的安全问题。
格式:
synchronized(对象)
{
需要被同步的代码,仅放被线程执行的易出现问题的那个部分,不要多放。
}
对象如同锁。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
同步的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
3,必须保证同步中只能有一个线程在运行。
其弊端:多个线程需要判断锁,较为消耗资源,
举例:
class Ticket implements Runnable
{
private int tick = 1000;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)//这里虽要一个对象用于标识锁。
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
}
同步函数:
函数用synchronized修饰后就可以实现同步了。同步函数的锁是this,也就是代表调用这个函数的对象。
如果同步函数被静态修饰后,因为静态方法中也不可以定义this,并且静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
所以静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class
格式:类名.class 该对象的类型是Class
举例:
写一个延迟加载的单例设计模式:
class DanLi
{
private static DanLi s = null;
private DanLi(){}
private static DanLi getInstance()
{
//用同步解决多线程用懒汉式的安全隐患,同步锁用的对象是该类的字节码对象。
//用双重判断解决多线程效率问题。关健在于用以双重判断不用再每次都走同步操作。
if(s==null)
{
synchronized(DanLi.class)
{
if(s==null)
s = new DanLi();
}
}
return s;
}
}
死锁:
同步中嵌套同步。
线程间通讯
就是多个线程在操作同一个资源,但是操作的动作不同。
需要用到的方法:
wait();注意:其会抛出一个异常,所以一定要try{this.wait();}catch(Exception e){}
notify();解除线程池中最先等待的那个wait,就是看谁等待时间最长,依次类推下去。
notifyAll();解除所有的wait等待。
1,这三个方法都使用在同步中,因为要对持有监视器(锁)的线程操作,而只有同步才具有锁。
2,这三个方法都属于Object类中,因为这些方法在操作同步中线程时,都必须要标识它们所操作线程仅有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
举例:
生产者和消费者实例:
jdk1.5中对synchronized同步功能作了升级,把其替换成了Lock操作,用lock.lock();加锁,用lock.unlock();来解锁。同时把等待唤醒机制中wait(),notify(),notifyAll(),功能封装成了一个Condition对象,用lock.newcondition()创建对象,并且用condition.await();及condition.signal();和condition.signalAll(),来实现等待,唤醒功能!而这个的优点就在于一个lock锁可以创建N多个condition对象,这就可以实现等待和唤醒指定对象。
public void set(String name)throws InterruptedException
{
lock.lock();加锁
try
{
while(flag)
condition_pro.await();//等待
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
condition_con.signal();唤醒特定对象。
}
finally
{
lock.unlock();//释放锁:这个动作一定要执行。
}
}
中断线程
Thread类中有一个方法叫做interrupt();中断线程。
其主要作用是强制唤醒处于wait()和sleep()状态中的线程,并抛出一个InterruptedException异常。所以使用时要进行处理:用try{},catch(InterruptedException e){}
守护线程
setdaemon(ture)方法:守护线程,也就是后台线程,要在线程运行前加载。
特点为:当只剩下守护线程时,java虚拟机自动退出。
应用:输入出输出,不再输入了,输出就没有意义,所以可以把输出线程标记为守护线程,那么输入线程也就是前台线程运行结束,这个时候输出线程也会自动结束。
等待线程中止
join();
一旦线程调用它,那么主线程便会释放cpu执行权,直到调用它的线程运行完,并抛出一个异常,主线程才会激活继续运行。这个可以用来插入线程。
提高权限优先级
setPriority();方法,总共有1—10,因为是常量,所以给一个名称,例如给最高的优先级。则为线程名.setPriority(Thread.MAX_PRIORITY);
暂停当前执行的线程,让其它线程运行。
Thread.yield();这样可以让线程交替运行,平均执行权。
当一个主函数里有多个循环或运行程序,则可使用匿名内部类封装成多个线程。这样程序高效,不干扰。