Java面向对象总结
面向对象的三大基本特征:继承性,封装性,多态性
别人问你对象是什么,类是什么?你会怎么回答?
对于基本概念,自己的心里要有一个思路
要了解对象要先知道类,引用一句书本的概念:类用于描述客观世界里某一类对象的共同特征,而对象则是类的具体存在。
所以你的回答可以是:许多对象的共同特征的抽象概念就是类,而对象则是类的具体存在。
用代码来,解释一下:比如People类,有类成员:变量name和方法eat();
这里的People类只是一个抽象的概念,不能有实际的行为eat,这时候就需要实例化对象people来表现实际的行为。
实例化:People xiaoming=new People();
怎么理解这个表达式呢?左边是实例化还是右边是实例化呢?
其实右边才是实例化,然后把实例化的东西,赋值给左边的People类对象的引用地址
然后就有了People类的实例化对象xiaoming
对象就可以有实际行为了:
xiaoming.name= ”小明 ”;//赋予名字
xiaoming.eat();//执行某些操作
这里实例化就会运行类的构造方法,
比如你在另一个类的方法里面只编写new People(),即使没有赋值对象,也会运行People里面的构造方法,
重写一下People的构造方法,并在里面输出一句话,运行,就可以看到输出的语句。
借助网上一个例子解释,面向对象的思想:
所谓面向对象,书上说的都是关于对象的概念,这东西很抽象,难以理解,很简单的一个思路,如,我要木头,从建材市场这个对象拿,调用建材市场这个对象中出售木头这个方法,返回我一个木头。至于建材市场的木头哪来的,我不知道,也不用知道,这个社会有各行各业,他们全都是对象,都有一个方法,返回该职业能返回给社会的事物,之所以说面向对象更接近于人的正常思维,妙处就在这,我们把能拿到我们想要的东西的出处抽取成一个对象,我们直接访问它,拿到我们要的东西,它又从它该拿到东西的对象处拿东西,看似互不相干,却又有很多关系,这就是面向对象,记住,每一个对象,一个方法,只做它自己该做的事,其他的,交给其他的方法,其他的对象,层次理清楚,你能写出很棒的程序。
面向对象主要的内容:类和对象的关系,封装(机箱故事),继承,多态,构造函数,this,supper,static,内部类(用处很少),抽象类,接口
面向对象是一种思想,能让复杂的问题简单化,让我们角色从执行者变成指挥者,不要知道过程,只要知道结果。(一切皆对象。)
对象的特点在于封装数据,数据包含着属性和行为。
一般一个程序可以由很多个类组成,也可以有多个主函数,但一般有一个主函数就可以。
格式:引用类名对象名=new构造函数名(参数);
例:class DuiXiang{}
则建立对象为:DuiXiang mingZi=new DuiXiang();
如果要实现功能,则格式为:
mingZi.变量或函数名();
函数是最小的封装体。类也是一个封装体。
private:私有的,权限修饰符,用来修饰类中的成员(成员变量 、成员函数),其只在本类中有效。
每个成员变量通常都会对应两个访问方式:一个设置,一个获取。
注意:私有仅是封装的一种表现形式。
因为语句都必须在函数内,所以,私有化后,再提供访问方式,就可以在访问方式的函数内实现变量控制。这样就提高代码的健壮性。
一般进行封装,要对类中的大部分属性都隐藏,最低权限是private。
类里有一个setXxx函数(一般返回值是void,保存数据,要带参数)和getXxx函数(有返回值类型,但一般没参数,getXxx之后一般用一个变量来接收:stringx=p.getXxx),那代表一定有一个私有化属性。
成员变量都有初始化值, 局部变量可以没有。
举例:
class XueSheng
{
private Stringname;
public void setName(Stringname)//这个函数方便以后赋值
{
this.name=name;
}
public String getName()//这个函数是方便以后有需要调用的,比如以后要打印name的值
{
return name;
}
函数名与类名一致,不用返回值类型,不可以用return,主要用来初始化对象。
对象一建立,就默认调用其构造函数。一个对象一建立,就有其默认的属性和行为。(如一个人刚出生就会呼吸,哭...)。
如果我们没有指认,那么类建立就会默认建一个构造函数(类名(){}),不然对象无法初始化,也就无法建立。(注意:只要我们有重写,那么就不会使用默认的建构造函数)
其与普通函数的区别除了写法上之外:
1,构造函数只在对象建立时运行一次,不再运行了,而普通函数可以调用多次,另外,构造函数是用来初始化对象的,而一般方法是用来添加对象具备的功能。
何时我们要手动添加构造函数:当我们分析事物时,该事物存在一些特性或行为,那么我们就给其定义一个构造函数,如果有变量参与运算,那么我们就给构造函数定义一个参数。
构造函数写法举例:
class Person
{
Person(String name,int age)
//注意:在新建一个对象时要加上参数进行区分,因为一个类可以放很多个构造方法。
//比如调用方式:Person p =new Person("fada",20)//构造函数已经被重写了,默//认的构造方法已经不存在了,再调用 Person p=new Person();会报错
{
this.name = name;
this.age = age;
}
1,所有对象在建立时都先执行构造代码块初始化,再执行构造函数初始化。
2,作用:当所有对象有共性时,那么就可以定义一个构造代码块(例如:所有小孩先出来就是哭,然后才有其它的属性)
构造代码块的写法(就是在类里面用一个大括号)举例:
class Person
{
{
System.out.print("fada")
//这样一写,那么以后每次建立一个构造函数时便先初始化这个构造代码块
}
}
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去引用)
例:
classPerson
{
private String name;
private int age;
Person()
{
System.out.println("personrun");
}
Person(String name)
{
This();
this.name =name;
}
}
二、super
super关键和this作用类似,是被屏蔽的成员变量或者成员方法或变为可见,或者说用来引用被屏蔽的成员变量和成员成员方法。
不过super是用在子类中,目的是访问直接父类中被屏蔽的成员,注意是直接父类(就是类之上最近的超类)。下面是一个综合运用super的例子,有两个类:一个Father类,一个Father类的子类Son,通过这两个类完全演示了super的用法,一下是代码:
public class Father {
public String v="Father";
public String x="输出了Father类的public成员变量x!!!";
public Father() {
System.out.println("Father构造方法被调用!");
}
public Father(String v){
this.v="Father类的带参数构造方法!运行了.";
}
public void outinfo(){
System.out.println("Father的outinfo方法被调用");
}
public static void main(String[] args) {
// TODO 自动生成方法存根
}
}
public class Son extends Father{
public String v="Son";
public Son() {
super(); //调用超类的构造方法,只能放到第一行.
System.out.println("Son无参数构造方法被调用!");
//super(); //错误的,必须放到构造方法体的最前面.
}
public Son(String str){
super(str);
System.out.println("Son带参数构造方法被调用!");
}
//覆盖了超类成员方法outinfo()
public void outinfo(){
System.out.println("Son的outinfo()方法被调用");
}
public void test(){
String v="哈哈哈哈!"; //局部变量v覆盖了成员变量v和超类变量v
System.out.println("------1-----");
System.out.println(v); //输出局部变量v
System.out.println(this.v); //输出(子类)成员变量v
System.out.println(super.v); //输出超类成员变量v
System.out.println("------2-----");
System.out.println(x); //输出超类成员变量v,子类继承而来
System.out.println(super.x); //输出超类成员变量v
System.out.println("------3-----");
outinfo(); //调用子类的outinfo()方法
this.outinfo(); //调用子类的outinfo()方法
super.outinfo(); //调用父类的outinfo()方法
}
public static void main(String[] args) {
new Son().test();
}
}
子类Son运行结果:
Father构造方法被调用!
Son无参数构造方法被调用!
------1-----
哈哈哈哈!
Son
Father
------2-----
输出了Father类的public成员变量x!!!
输出了Father类的public成员变量x!!!
------3-----
Son的outinfo()方法被调用
Son的outinfo()方法被调用
Father的outinfo方法被调用
说明:例子仅仅为了说明super的用法,实际在设计类的时候一般都尽可能私有(private)化。
通过上面的例子,下面总结一下super的用法:
第一、在子类构造方法中要调用父类的构造方法,用“super(参数列表)”的方式调用,参数不是必须的。同时还要注意的一点是:“super(参数列表)”这条语句只能用在子类构造方法体中的第一行。
第二、当子类方法中的局部变量或者子类的成员变量与父类成员变量同名时,也就是子类局部变量覆盖父类成员变量时,用“super.成员变量名”来引用父类成员变量。当然,如果父类的成员变量没有被覆盖,也可以用“super.成员变量名”来引用父类成员变量,不过这是不必要的。
第三、当子类的成员方法覆盖了父类的成员方法时,也就是子类和父类有完全相同的方法定义(但方法体可以不同),此时,用“super.方法名(参数列表)”的方式访问父类的方法。
this、super的用法也不过这些,只有理解了其中的原理,才不会跌入陷阱!
static是一个修饰符:
三种修饰:修饰类的变量、方法和构造代码块。静态方法只能直接引用和访问静态变量和方法
注意(函数即方法,对象也叫实例)
有时你希望定义一个类成员,使它的使用完全独立于该类的任何对象。通常情况下,类成员必须通过它的类的对象访问,但是可以创建这样一个成员,它能够被它自己使用,而不必引用特定的实例。在成员的声明前面加上关键字static(静态的)就能创建这样的成员。如果一个成员被声明为static,它就能够在它的类的任何对象创建之前被访问,而不必引用任何对象。你可以将方法和变量都声明为static。static 成员的最常见的例子是main() 。因为在程序开始执行时必须调用main() ,所以它被声明为static。 声明为static的变量称为静态变量或类变量。可以直接通过类名引用静态变量,也可以通过实例名来引用静态变量,但最好采用前者,因为后者容易混淆静态变量和一般变量。静态变量是跟类相关联的,类的所有实例共同拥有一个静态变量。关键点:静态变量与实例变量的区别:静态变量也叫类变量,为所有对象所共有,所以一个对象的变量值改变,那么所有对象的值一起发生改变。
而实例变量则是各自独立的不受影响。
声明为static的方法称为静态方法或类方法。静态方法可以直接调用静态方法,访问静态变量,但是不能直接访问实例变量和实例方法。静态方法中不能使用this关键字,因为静态方法不属于任何一个实例。静态方法不能被子类的静态方法覆盖。
特有属性随着对象存储于堆内存中,而static修饰后的属性,存在于方法区.
什么时候使用静态修饰符?
当对象中出现共享数据时,使用静态修饰。
但对象内的特有数据要定义成非静态存在于堆内存中。
而对于方法时:
当功能内部没有访问到非静态数据时,(即对象的特有数据)
那么可以使用静态修饰。
静态代码块:
用于给类做初始化的。不管有没有对象都执行,只要调用了类里面成员或方法,就会执行,而且优先于主函数,优先执行级别最高。
例:
class JingTai
{
static intage;
staticString name;
static//静态代码块初始化类的,所以最先运行。
{
System.out.println("name="+name);
}
//下面是构造代码块,第二个运行
{
System.out.println("age="+age);
}
publicstatic void jing(int x)//被对象调用,第三个运行。
{
x=age+1;//因为age是静态的,所以能调用
System.out.println("x="+x);
}
}
class FaDa
{
public staticvoid main(String[] args)
{
JingTaip =new JingTai();
p.jing(2);
}
}
继承就是将两个类的共性描述提取出来,单独进行描述,从而简化代码,提高复用性。
格式:class 子类名extends 父类名
关健字为extends:继承
判断两个类是否有所属关系,那么我们就继承一下,看父类所具有的内容属性,子类是否全都需要具备。如果是,那么他们所属关系成立,就是继承。
注意:千万不要为了获取其他类的功能,简化代码而继承。
必须是类与类之间有所属关系才可以继承。所属关系 is a。
例如:
class A
{
//voiddemo1(){}
voiddemo2(){}
}
class B
{
//voiddemo1(){}
voiddemo3(){}
}
我们可以让B继承下A,但发现,A中还具有demo2这个方法是B所不具备的,所以他们俩的继承关系不成立,但我们可以把两个类的共性提取出来,成为一个
class C
{
void demo1(){}
}
那么A和B就能够继承C,这样就实现在代码的简化和复用。
Java语言中:java只支持单继承,不支持多继承。
因为多继承容易带来安全隐患:当多个父类中定义了相同功能,
当功能内容不同时,子类对象不确定要运行哪一个。
但是java保留这种机制。并用另一种体现形式来完成表示。多实现。
事物与事物的另一种关系,比如(类与类,对象与对象)
聚合:例如:一个班里的有很多学生,那么班级和学生就是聚合关系。就是包含。
组合:组合与聚合区别在于,组合各成员是不可分割的,否则会缺少功能,但聚合里的成员可以脱离,对整体没有影响。
类中成员:
1,变量。
2,函数。
3,构造函数。
1,变量
如果子类中出现非私有的同名成员变量时,
子类要访问本类中的变量,用this
子类要访问父类中的同名变量,用super。
super的使用和this的使用几乎一致。
this代表的是本类对象的引用。
super代表的是父类对象的引用。
例如:
class Fu
{
intnum = 4;
}
class Zi extends Fu
{
intnum = 5;
voidshow()
{
System.out.println(super.num);
//有super对象调用函数打印结果是4,没有打印结果是5
}
}
当子类继承父类,沿袭了父类的功能,这时不但可以保留父类的功能定义,还可以重写功能内容。
覆盖要注意的关健点:
1,子类覆盖父类,必须保证子类访问权限大于等于父类权限,才可以覆盖,否则编译失败。
(权限修饰符没设的时候为默认权限,介于public与private之间。)
例如:
class Fu
{
void show()
{
System.out.println("fushow");
}
}
class Zi extends Fu
{
void show()
{
System.out.println("java");
//这里我即可以改变,也可以增加。增加时我们不用重写一遍父类函数的内容,只虽要super.函数名()就可以。
}
2,静态只能覆盖静态。
注意:
重载:只看同名函数的参数列表。
重写:子父类方法要一模一样。
子类中的构造函数会默认调用父类中的函数中的默认构造函数,因为子类的构造函数默认第一行有一条隐式的语句 super();
但有几个地方要注意:
例:
class Fu
{
Fu()
注意:如果没有这一个空参数的构造函数,但又有我们指认的非空参数的构造函数,那么我们知道默认那个空参数构造函数也不会生成,这个时候子类就会编译失败。这个时候就不能让子类默认获取super();而要指认super(x)
{
num= 60;
System.out.println("furun");
}
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,而且必须放在第一行
子类中至少会有一个构造函数会访问父类中的构造函数。
extendsObject:java中的顶极父类,也就是任何没标明父类的类的默认父类。
1,可以修饰类,函数,变量。
2,被final修饰的类不可以被继承。作用:为了避免被继承,被子类复写功能。
3,被final修饰的方法不可以被复写。
4,被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,有可以修饰局部变量。
当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读。
而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有字母都大写,如果由多个单词组成,单词间通过_连接。
5,内部类定义在类中的局部位置上是,只能访问该局部被final修饰的局部变量。
例如:
final int x=4;那么x永远等于4,不可以被赋值,就成了常量,之所以不直接定常量,是因为要给数值起个名,增强阅读性,以后也好调用。
特点:
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 Studentextends Employee
{
Student(Stringname,int age)
{
super(name,age);//直接调用父类构造函数。
}
public voidwork()
{
System.out.println("shangke");
}
}
class LaoShi extendsEmployee
{
LaoShi(String name,int age)
{
super(name,age);//直接调用父类构造函数。
}
public void work()
{
System.out.println("laoshi");
}
}
什么是模版方法呢?
在定义功能时,功能的一部分是确定的,但是有一部分是不确定,而确定的部分在使用不确定的部分,
那么这时就将不确定的部分暴露出去。由该类的子类去完成。
abstract classGetTime
{
public final voidgetTime()//这个类是不需要改变的,所以就用final最终修饰。
{
long start =System.currentTimeMillis();
runcode();
long end =System.currentTimeMillis();
System.out.println("毫秒:"+(end-start));
}
public abstract void runcode();//这个函数主体要设成不确定,这样记算任何函数的运行时间:我只要把要算的函数定义成这个的内容就行了。所以设成抽象类的方法。
}
class SubTimeextends 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 Demoimplements JieKou,JieKou2
3,接口在可以实现多继承,如:
interface Test extends A,B
例:
abstract class Student
{
abstract void study();学生都要学习,所以可以作为父类来给子类继承。
}
interface Smoking
{
void smoke();//抽烟不是所有学生都会,所以,不可以继承,不然所有学生都抽烟了,所以定义为接口,让需要的类实现就行。
}
class ZhangSanextends Student implements Smoking
//继承后再实现,这样就实现了功能的扩展。
{
void study(){}
public void smoke(){}//记得要复写,因为父类是抽象的。
}
class Lisi extendsStudent
{
void study()
{
System.out.prtintln("study")
}
}
1,多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。
2,多态的前提
必须是类与类之间有关系。要么继承,要么实现。
通常还有一个前提:存在覆盖。
3,多态的好处
多态的出现大大的提高程序的扩展性。
4,多态的弊端:
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
如何要访问子类特有对象必须强制转换为子类对象。
abstract classAnimal
{
abstract void eat();
}
class Cat extendsAnimal
{
public void eat()
多态还有一个前提:存在覆盖。不然没有意义,只是继承了一个空方法。
{
System.out.println("吃鱼");
}
public void catchMouse()
但是只能使用父类的引用访问父类中的成员。因为父类中并没有catchMouse方法,所以多态无法实现catchMouse
{
System.out.println("抓老鼠");
}
}
class Dog extendsAnimal
{
public void eat()
{
System.out.println("吃骨头");
}
}
public static voidfunction(Animal a)//Animal a = new Cat();父类的引用指向了自己的子类对象。父类的引用也可以接收自己的子类对象。
前题:必须是类与类之间有关系。要么继承,要么实现。Animal是动物,Cat是它的子类。
{
a.eat();
}
class DuoTaiDemo
{
public static void main(String[] args)
{
function(new Cat());
function(new Dog());提高了扩展性,不用再去新建一个对象,再引用对象的功能。
}
何为继承,有何作用
继承就是将两个类的共性描述提取出来,单独进行描述,从而简化代码,提高复用性。
格式:class 子类名extends 父类名
关健字为extends:继承
判断两个类是否有所属关系,那么我们就继承一下,看父类所具有的内容属性,子类是否全都需要具备。如果是,那么他们所属关系成立,就是继承。
注意:千万不要为了获取其他类的功能,简化代码而继承。
必须是类与类之间有所属关系才可以继承。所属关系 is a。
例如:
class A
{
//voiddemo1(){}
voiddemo2(){}
}
class B
{
//voiddemo1(){}
voiddemo3(){}
}
我们可以让B继承下A,但发现,A中还具有demo2这个方法是B所不具备的,所以他们俩的继承关系不成立,但我们可以把两个类的共性提取出来,成为一个
class C
{
void demo1(){}
}
那么A和B就能够继承C,这样就实现在代码的简化和复用。
Java语言中:java只支持单继承,不支持多继承。
因为多继承容易带来安全隐患:当多个父类中定义了相同功能,
当功能内容不同时,子类对象不确定要运行哪一个。
但是java保留这种机制。并用另一种体现形式来完成表示。多实现。
java支持多层继承。也就是一个继承体系
如何使用一个继承体系中的功能呢?
想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能。
通过了解共性功能,就可以知道该体系的基本功能。
那么这个体系已经可以基本使用了。
那么在具体调用时,要创建最子类的对象,为什么呢?
一是因为有可能父类不能创建对象,
二是创建子类对象可以使用更多的功能,包括基本的也包括特有的。
简单一句话:查阅父类功能,创建子类对象使用功能。
是所有对象的直接后者间接父类.
该类中定义的是所有对象都具备的功能。
如果自定义类中需要的功能是Object含有的,那么没有必要重新定义,只要沿袭父类中的功能,建立自己特有比较内容,覆盖即可。
常用的有比较功能:
public booleanequals(Object obj)
//这里要是Object对象,所以这里相当于:Object obj =new 自定义类()
{
......这里写其自定义的内容,如果这里要有自定义类里的特有功能,那么要强转对象
格式是:自定义类 x = (自定义类)obj;
}
下面有些只是应用较少,或者涉及到的知识会有点复杂,可以根据自己的兴趣了解。
特点:1,内部类可以直接访问外部类中的成员,包括私有。
(原因:内部类中持有了一个外部类的引用,格式:外部类名.this)
2,外部类要访问内部类,必须建立内部类对象。
3, 当内部类在成员位置上,就可以被成员修饰符所修饰。
特点:
1,匿名内部类其实就是内部类的简写格式。
2,定义匿名内部类的前提:
内部类必须是继承一个类或者实现接口。
3,匿名内部类的格式: new 父类或者接口(){定义子类的内容}
4,其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。 可以理解为带内容的对象。
5,匿名内部类中定义的方法最好不要超过3个。
把程序运行中出的问题通过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,对捕获到的异常对象进行常见方法操作。
StringgetMessage():获取异常信息。
在函数上声明异常。
便于提高安全性,让调用出进行处理。不处理编译失败
对多异常的处理。
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(Stringmessage)
{
super(message);
//直接调用父类方法,用来返回此throwable的详细消息字符串
}
}
class TingDianException extends RuntimeException
{
TingDianException(Stringmessage)
{
super(message);
}
}
class Computer
{
privateint state = 2;
publicvoid run()throws LanPingException
{
if(state==2)
thrownew LanPingException("蓝屏了");
if(state==3)
thrownew TingDianException(" 停电了");
System.out.println("电脑运行");
}
publicvoid reset()
{
state= 1;
System.out.println("电脑重启");
}
}
class Person
{
privateString name;
privateComputer cmpt;
Person(Stringname)
{
this.name= name;
cmpt= new Computer();
}
publicvoid prelect()
{
try
{
cmpt.run();
//人要调用run(),所以其接受了异常,其要么抛出,要行么处理。
}
catch(LanPingException e)
{
System.out.println("电脑蓝屏啦");
cmpt.reset();
}
System.out.println("办公");
}
}
class ExceptionTest
{
publicstatic void main(String[] args)
{
Persont = new Person("人");
t.prelect();
}
}
异常的处理要注意的关健点:
1,处理方式有两种:try 或者 throws。
2,调用到抛出异常的功能时,抛出几个,就处理几个。
一个try对应多个catch。
3,多个catch,父类的catch放到最下面。
4,catch内,需要定义针对性的处理方式。不要简单的定义printStackTrace,输出语句。
也不要不写。
当捕获到的异常,本功能处理不了时,可以继续在catch中抛出。
try
{
thrownew AException();
}
catch(AException e)
{
throwe;
}
如果该异常处理不了,但并不属于该功能出现的异常。
可以将异常转换后,在抛出和该功能相关的异常。
或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,
当调用者知道。并处理。也可以将捕获异常处理后,转换新的异常。
try
{
thrownew AException();
}
catch(AException e)
{
//对AException处理。
thrownew BException();
}
异常在子父类覆盖需要注意的问题:
1,子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类。
2,如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。
3,如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。
如果子类方法发生了异常。就必须要进行try处理。绝对不能抛。
包的作用:
可以将源文件与class文件分离,有利于安全性。也可以打包并可以直接操作。
两个关健字
package :用来标识class文件其属于哪个包,放于代码的最上面:package包名(包名全部小写)
import :用于导入,就是要以把包下面的具体的一个类导入,(例如:import.a.b....)以及可以把包下面所有的类导入(例如:import.a.*;)
例:
package bao2;
import bao1.Test;
class Test1
{
publicstatic void main(String[] args)
{
Testa = new Test();
a.show();
System.out.println("fadatest1");
}
}
编译格式:
到源文件目录: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
线程:每一个进程都有一个执行顺序,这个顺序就是一个独立的控制单元,这个独立的控制单元就是线程,线程在控制着进程的执行。
由一个Thread类来对这类事物进行描述。
自定义一个进程的方法(1):
定义一个类继承Thread类,并且复写Thread类中的run方法,将自定义代码储存在run方法中,让线程运行。最后通过创建一个线程的对象,通过调用线程的start()方法,来启动线程并执行run方法。
例:
class Xian extends Thread
{
publicvoid run()
{
for(intx=0; x<100; x++)
System.out.println("xianchengrun----"+x);
}
}
class Cheng
{
publicstatic void main(String[] args)
{
Xiand = new Xian();//创建一个Thread类或其子类的对象就是建立一个线程。
d.start();//开启线程并执行该线程的run方法。
//d.run();//如果这样写仅仅是对象调用方法。而线程创建了,并没有运行。
}
}
线程都有自己默认的名称,因为其有setName和getName方法。
默认为:Thread-编号 该编号从0开始。
设置线程名称:setName或者构造函数。
例:Test(Stringname)
{
super(name);//直接调用父类方法
}
创建对象设置名字:Testa1 = new Test("xiancheng1"); Test a1 = new Test("xiancheng2");
通过staticThread 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//extendsThread
{
private int tick = 100;
publicvoid run()
{
while(true)//让其循环
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+"....sale: "+ tick--);
}
}
}
}
class TicketDemo
{
publicstatic void main(String[] args)
{
Tickett = new Ticket();
Threadt1 = new Thread(t);
//创建了一个线程;Thread里默认有一个构造方法用来接收Runnable对象。
这样一来Thread对象里面就有了Ticket对象。就能运行自定义run方法。
Threadt2 = new Thread(t);//创建了一个线程;
Threadt3 = new Thread(t);//创建了一个线程;
Threadt4 = new Thread(t);//创建了一个线程;
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步代码块:
作用:用于解决多线程的安全问题。
格式:
synchronized(对象)
{
需要被同步的代码,仅放被线程执行的易出现问题的那个部分,不要多放。
}
对象如同锁。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
同步的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
3,必须保证同步中只能有一个线程在运行。
其弊端:多个线程需要判断锁,较为消耗资源,
举例:
class Ticket implements Runnable
{
private int tick = 1000;
Objectobj = new Object();
publicvoid run()
{
while(true)
{
synchronized(obj)//这里虽要一个对象用于标识锁。
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exceptione){}
System.out.println(Thread.currentThread().getName()+"....sale: "+ tick--);
}
}
}
}
}
同步函数:
函数用synchronized修饰后就可以实现同步了。同步函数的锁是this,也就是代表调用这个函数的对象。
如果同步函数被静态修饰后,因为静态方法中也不可以定义this,并且静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
所以静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class
格式:类名.class 该对象的类型是Class
举例:
写一个延迟加载的单例设计模式:
class DanLi
{
privatestatic DanLi s = null;
privateDanLi(){}
privatestatic DanLi getInstance()
{
//用同步解决多线程用懒汉式的安全隐患,同步锁用的对象是该类的字节码对象。
//用双重判断解决多线程效率问题。关健在于用以双重判断不用再每次都走同步操作。
if(s==null)
{
synchronized(DanLi.class)
{
if(s==null)
s= new DanLi();
}
}
returns;
}
}
死锁:
同步中嵌套同步。
线程间通讯
就是多个线程在操作同一个资源,但是操作的动作不同。
需要用到的方法:
wait();注意:其会抛出一个异常,所以一定要try{this.wait();}catch(Exceptione){}
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(InterruptedExceptione){}
守护线程
setdaemon(ture)方法:守护线程,也就是后台线程,要在线程运行前加载。
特点为:当只剩下守护线程时,java虚拟机自动退出。
应用:输入出输出,不再输入了,输出就没有意义,所以可以把输出线程标记为守护线程,那么输入线程也就是前台线程运行结束,这个时候输出线程也会自动结束。
等待线程中止
join();
一旦线程调用它,那么主线程便会释放cpu执行权,直到调用它的线程运行完,并抛出一个异常,主线程才会激活继续运行。这个可以用来插入线程。
提高权限优先级
setPriority();方法,总共有1—10,因为是常量,所以给一个名称,例如给最高的优先级。则为线程名.setPriority(Thread.MAX_PRIORITY);
暂停当前执行的线程,让其它线程运行。
Thread.yield();这样可以让线程交替运行,平均执行权。
当一个主函数里有多个循环或运行程序,则可使用匿名内部类封装成多个线程。这样程序高效,不干扰。
上面是java面向对象和类涉及到的一些知识,有自己写的,也有网上借鉴的,有的是比较基础的,也有的是复杂点的,可以根据自己的情况适当阅读。