--------------- android培训、java培训、期待与您交流! ----------------
面向对象(封装、继承、多态)
1 封装
封装:隐藏对象的属性和细节,仅对外提供公共的访问方式。
好处:
1、将变化隔离,提高安全性
2、便于使用,提高重用性
原则:
1、将不需要对外提供的内容都隐藏起来
2、把属性都隐藏,提供公共方法对其进行访问
public class Test {
public static void main(String[] args) {
Student t=new Student();//创建对象
t.setAge(30);
System.out.println("age="+t.getAge());
t.setAge(140);
}
}
class Student//创建student
{
//private:权限修饰符,私有,不希望别人直接访问赋值,通过私有化把属性进行隐藏
private int age;
//对外提供方法访问隐藏的属性
public void setAge(int age)
{
//进行条件限制
if(0<age&&age<120)
this.age=age;
else
System.out.println("年龄设置错误");
}
//对外提供方法访问隐藏属性
public int getAge()
{
return age;
}
}
运行结果:
ps.
1.封装不等于私有,私有仅仅是封装的一种表现形式
2.之所以对外提供访问方式,是通过在访问方式中加入逻辑判断语句,操作数据,提高代码的健壮性
【】单例设模式
设计模式:对问题行之有效的解决方式,是一种思想
单例设计模式:保证一个类在内存中对象的唯一性
PS.
当多个程序使用一个配置对象时就需要保证该对象的唯一性
确保对象唯一性思路(代码体现):
1、禁止其他程序new一个该类对象(将构造函数私有化)
2、本类中自定义一个对象,让其他程序可以访问该类对象(在类中创建一个本类对象)
3、对外提供访问方式,让其他程序访问(提供方法获取该对象)
饿汉式
<span style="font-size:12px;">class Single
{
private int num;
public void setNum(int num)
{
this.num=num;
}
public int getNum()
{
return this.num;
}
private Single(){}//将构造函数私有化,其他程序无法创建该类对象
static Single s=new Single();//创建一个本类对象
//需要被其他程序调用,但是其他程序不能创建该类对象,所以要定义为静态用类名调用
public static Single getInstance()
{
return s;
}
}
public class Test {
public static void main(String[] args)
{
Single s1=Single.getInstance();//通过类名调用静态方法
Single s2=Single.getInstance();
System.out.println(s1==s2); //验证对象的唯一性
s1.setNum(10);
System.out.println(s2.getNum());//验证对象的唯一性
}
}</span>
运行结果:
PS.
之所以不用Single.s;的方式获取Single对象,而采用getInstance获取是因为在getInstance方法中我们可以做一些判断来决定是否返回Single的对象,也就是实现了对单例对象的可控。所以,给Single的构造方法加上了private限制,禁止使用者直接采用Single.s;的方式获取。
<span style="font-size:12px;">class Single
{
private int num;
public void setNum(int num)
{
this.num=num;
}
public int getNum()
{
return this.num;
}
private Single(){}//将构造函数私有化,其他程序无法创建该类对象
static Single s=null;//没有对象,调用getInstance时才创建对象
//需要被其他程序调用,但是其他程序不能创建该类对象,所以要定义为静态用类名调用
public static Single getInstance()
{
if(s==null)
s=new Single();
return s;
}
}
public class Test {
public static void main(String[] args)
{
Single s1=Single.getInstance();//通过类名调用静态方法
Single s2=Single.getInstance();
System.out.println(s1==s2); //验证对象的唯一性
s1.setNum(10);
System.out.println(s2.getNum());//验证对象的唯一性
}
}</span>
运行结果:
2 继承
继承:(extends 类名)
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。
1、提高代码的复用性
2、继承让类与类之间产生了关系,有了这个关系,才有多态的关系。
ps.
1、子类可以直接访问父类非私有的成员
2、子类无法继承父类私有的成员
<span style="font-size:12px;">class Person
{
int age=0;
String name="baby";
}
class Student extends Person//Student继承Person类
{
public void study(){
System.out.println(name+"...Student study....");//子类可以直接访问父类非私有成员
}
}
public class Test {
public static void main(String[] args)
{
Student s=new Student();
s.age=3;
System.out.println("name:"+s.name+"...age:"+s.age);
s.study();
}
}
</span>
运行结果
PS.
继承提高了代码的复用性
PS.
java只支持单继承,不支持多继承,但支持多实现
这是因为多继承存在安全隐患,当多个父类有相同的方法时,java不知道执行哪一个
PS.
1、使用继承体系,先查阅体系父类的描述,因为父类找那个定义的是该体系中共性功能
2、在具体调用时,要创建子类的对象,因为a.父类可能不能创建对象b.创建子类对象可以使用更多的功能,包括基本的也包括特有的。
归纳:查阅父类功能,创建子类使用功能。
子父类中成员的特点
1、成员变量:子父类中类成员的特点:如果子类中出现非私有的同名成员变量时,访问本类变量用this,访问父类变量用super。
<span style="font-size:12px;">class Person
{
int age=5;
}
class Student extends Person//Student继承Person类
{
int age=7;
public void show(){
System.out.println(this.age+"..."+super.age);//子类可以直接访问父类非私有成员
}
}
public class Test {
public static void main(String[] args)
{
Student s=new Student();
s.show();
}
}</span>
运行结果:7...3
2、成员函数:子类出现和父类一样的函数时,子类对象调用函数会运行子类函数内容覆盖父类函数内容(因此在定义新功能时可以使用覆盖,保留父类的功能定义并重写功能内容)
<span style="font-size:12px;">class Person
{
public void show(){
System.out.println("person run...");
}
}
class Student extends Person//Student继承Person类
{
public void show(){
System.out.println("student run...");//子类重载父类的show方法
}
}
public class Test {
public static void main(String[] args)
{
Student s=new Student();
s.show();//运行的是子类的重载后的show方法
}
}</span>
运行结果
PS.
在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取
<span style="font-size:12px;">class Phone{
void call(){}
void show(){
System.out.println("number" );
}
}
class NewPhone extends Phone{
void show(){
super.show(); //获取父类被重载的方法
System.out.println("name" );
System.out.println("pic" );
}
}
public class Test {
public static void main(String[] args)
{
NewPhone p = new NewPhone();
p.show();
}
}</span>
PS.
a、子类覆盖父类,必须保证子类权限大于父类权限
b、父类的private方法不能被覆盖
b、重载(同一个类中):只看同名函数的参数列表 重写(子类中重写父类):子父类方法要一模一样
3、构造函数:在子类的对象进行初始化时,父类构造函数也会运行,因为子类的构造函数默认第一行隐示语句super(),而且子类中所有的构造函数默认第一行都是super()
<span style="font-size:12px;">class Fu{
Fu()
{
System.out.println("fu run....");
}
}
class Zi extends Fu{
Zi()
{
//super();子类的第一行默认有一个访问父类构造函数的隐式语句
System.out.println("zi run....");
}
}
public class Test {
public static void main(String[] args)
{
Zi z = new Zi();
}
}</span>
运行结果:
那么为什么子类一定要访问父类的构造函数呢?
因为父类中的数据子类可以直接获取,所以子类对象建立时,需要先查看父类是如何对这些数据进行初始化的。
<span style="font-size:12px;">class Fu//父类没有空参数的构造函数
</span><span style="font-size:12px;">{
Fu(int i)
{
System.out.println("fu run...."+i);
}
}
class Zi extends Fu{
Zi()
{
//super();子类的第一行默认有一个访问父类构造函数的隐式语句
//父类没有空参数的构造函数,因此此处要手动添加super(参数)来访问父类的指定构造函数
System.out.println("zi run....");
}
}
public class Test {
public static void main(String[] args)
{
Zi z = new Zi();
}
}</span>
运行结果:
PS.
1、当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。
2、子类构造函数中如果使用this调用了本类构造函数,那么默认的super();就没有了,因为super和this都只能定义在第一行,所以只能有一个。但是可以保证的是,子类中肯定会有其他的构造函数访问父类的构造函数。
3、super语句必须要定义在子类构造函数的第一行!因为父类的初始化动作要先完成。
对象的实例化过程
<span style="font-size:12px;">class Fu{
Fu(){
super();
//调用的是子类的show方法,此时其成员变量num还未进行显示初始化
show();
return;
}
void show(){
System.out.println("fu show" );
}
}
class Zi extends Fu{
int num = 8;
Zi(){
super();
//通过super初始化父类内容时,子类的成员变量并未显示初始化,等super()父类初始化完毕后,才进行子类的成员变量显示初始化
return;
}
void show(){
System.out.println("zi show..." + num);
}
}
class Test{
public static void main(String[] args){
Zi z = new Zi();
z.show();
}
}</span>
PS.
一个对象实例化过程,以Person p = new Person();为例:
1. JVM会读取指定的路径下的Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接的父类的情况下)。
2. 在内存中开辟空间,并分配地址。
3. 并在对象空间中,对对象的属性进行默认初始化。
4. 调用对应的构造函数进行初始化。
5. 在构造函数中,第一行会先到调用父类中构造函数进行初始化。
6. 父类初始化完毕后,再对子类的属性进行显示初始化。
7. 再进行子类构造函数的特定初始化。
8. 初始化完毕后,将地址值赋值给引用变量。
【】final关键字:
1、修饰符,可以修饰类、函数、变量
2、被final修饰的类不可以被继承。(final class 类名)
3、被final修饰的方法不可以复写。(final 函数名)
4、可以修饰成员变量,也可以修饰局部变量,被final修饰的变量是一个常量只能被赋值一次
5、 写法规范:常量所有字母都大写,多个单词,中间用_连接。
【】抽象类:abstract(把多个事物的共性和本质的内容抽取出来)
Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
抽象类特点:(和一般类无大区别,只是不确定部分需要子类明确)
1、抽象方法一定在抽象类中(格式:修饰符 abstract 返回值类型 函数名(参数列表) ;)
2、抽象方法和抽象类都必须被abstract关键字修饰
3、抽象类不可以用new创建对象,因为调用抽象方法没有意义
4、抽象类中的方法要被使用,必须由子类复写所有的抽象方法,建立子类对象调用(因此抽象类一定是父类)
PS.
抽象类和一般类的区别
A. 一般类有足够的信息描述事物。
抽象类描述事物的信息有可能不足。
B. 一般类中不能定义抽象方法,只能定义非抽象方法。
抽象类中可定义抽象方法,同时也可以定义非抽象方法。
C. 一般类可以被实例化。
抽象类不可以被实例化
PS.
抽象类可以不用定义抽象方法,仅仅是不让该类创建对象
<span style="font-size:12px;">/**
* 需求:
公司中程序员有姓名,工号,薪水,工作内容。
项目经理除了有姓名,工号,薪水,还有奖金,工作内容。
分析:
在这个问题领域中,通过名词提炼法:
程序员:
属性:姓名,工号,薪水。
行为:工作。
经理:
属性:姓名,工号,薪水,奖金。
行为:工作。
程序员和经理不存在着直接继承关系。
但是,程序员和经理却具有共性内容,可以进行抽取,因为他们都是公司的雇员。
可以将程序员和经理进行抽取,建立体系。
* */
abstract class Employee{
private String name,num;
private int pay;
Employee(String name,String num,int pay)
{
this.name=name;
this.num=num;
this.pay=pay;
}
abstract void work();
}
class Programmer extends Employee{
Programmer(String name,String num,int pay){
super(name,num,pay);
}
void work(){
System.out.println("程序员工作。。。");
}
}
class Manager extends Employee{
Manager(String name,String num,int pay){
super(name,num,pay);
}
public void work(){
System.out.println("经理工作。。。");
}</span>
<span style="font-size:12px;">}</span>
【】接口:(ingterface 类名)
特殊的抽象类,当抽象类中的方法都是抽象的,那么该类可以通过接口的形式表示,其中的常量和修饰符有固定的修饰符
常量:public static fin
方法:public abstract
interface inter{}
interface interA{}
class demo{}
class test extends demo implements inter,interA{}//多实现加继承,接口之间可以多继承。
接口特点:
a、接口是对外暴露的规则
b、接口是程序的功能扩展
c、接口可以用来多实现
d、类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口
e、接口与接口之间可以有继承关系。(继承和接口近似于属于的基本功能和扩展的功能区别,继承是 is a接口是like a)
P.S.
a、虽然抽象类中的全局变量和抽象方法的修饰符都可以不用写,但是这样阅读性很差。所以,最好写上。
b、类与类之间是继承关系,类与接口直接是实现关系。
c、接口不可以实例化,能由实现了接口并覆盖了接口中所有的抽象方法的子类实例化。否则,这个子类就是一个抽象类。
<span style="font-size:12px;">interface CC{
void show();
}
interface MM{
void method();
}
//接口与接口之间是继承关系,而且接口可以多继承
interface QQ extends CC,MM{
public void function();
}
class WW implements QQ{
//覆盖3个方法
public void show(){}
public void method(){}
public void function(){}
} </span>
抽象类和接口的异同点?
相同点:
都是不断向上抽取而来的。
不同点:
a、 抽象类需要被继承,而且只能单继承。
接口需要被实现,而且可以多实现。
b、抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法。
接口中只能定义抽象方法,必须由子类去实现。
c、 抽象类的继承,是is a关系,定义该体系的基本共性内容。接口的实现是like a关系。
3 多态
多态:事物存在的多种体现形态 animal c=new cat()//类型提升,向上转型>>>cat c=(cat)a//强制将父类的引用转成子类类型。
多态的体现:父类的引用指向了自己的子类对象,接受自己的子类对象(Animal a=new cat())
多态的前提:必须是类和类之间的关系,要么继承要么实现
好处:提高了代码的扩展性,前期定义的代码可以使用后期的内容。
弊端:前期定义的内容不能使用(调用)后期子类的特有内容。
<span style="font-size:12px;">abstract class Animal{
abstract void eat();
}
class Cat extends Animal{
void eat(){
System.out.println("猫吃鱼");
}
void catchMouse(){
System.out.println("猫抓老鼠");
};
}
class Dog extends Animal{
void eat(){
System.out.println("狗吃骨头");
}
void look(){
System.out.println("狗看家");
};
}
class Pig extends Animal{
void eat(){
System.out.println("猪吃菜");
}
void sleep(){
System.out.println("猪睡觉");
};
}
class Test{
public static void main(String[] args) {
function(new Cat());
function(new Dog());
function(new Pig());
}
public static void function(Animal a){
a.eat();
}
}</span>
运行结果:
<span style="font-size:12px;">abstract class Animal{
abstract void eat();
}
class Cat extends Animal{
void eat(){
System.out.println("猫吃鱼");
}
void catchMouse(){
System.out.println("猫抓老鼠");
};
}
class Dog extends Animal{
void eat(){
System.out.println("狗吃骨头");
}
void look(){
System.out.println("狗看家");
};
}
class Pig extends Animal{
void eat(){
System.out.println("猪吃菜");
}
void sleep(){
System.out.println("猪睡觉");
};
}
class Test{
public static void main(String[] args) {
function(new Cat());
function(new Dog());
function(new Pig());
}
public static void function(Animal a){<span style="font-family:微软雅黑;">//Animal a=new Cat()父类的引用指向子类对象,向上转型</span>
a.eat();<span style="font-family:微软雅黑;">//直接定义子类的特殊方法a.catchMouse会报错,因为Animal类中没有catchMouse方法</span>
if(a instanceof Cat)<span style="font-family:微软雅黑;">//instanceof-关键字(对象是否属于指定的类型)</span>
{
Cat c=(Cat) a;<span style="font-family:微软雅黑;">//向下强转型,始终都是子类类型在做变化</span>
c.catchMouse();<span style="font-family:微软雅黑;">//直接</span>
}
else if(a instanceof Dog)
{
((Dog) a).look();
}
else if(a instanceof Pig)
{
((Pig) a).sleep();
}
}
}</span>
运行结果:
多态中成员变量的特点
编译时:参考引用型变量所属的类中是否有调用的成员变量。有,编译通过,没有,编译失败。
运行时:参考引用型变量所属的类中是否有调用的成员变量,并运行该所属类中的成员变量。
简单说:编译和运行都参考等号的左边。
<span style="font-size:12px;">class Fu{
int num = 3;
}
class Zi extends Fu{
int num = 4;
}
class DuoTaiDemo{
public static void main(String[] args){
Zi f1 = new Zi();
System.out.println(f1.num);<span style="font-family:微软雅黑;">//调用的子类的成员变量</span>
Fu f2 = new Zi();
System.out.println(f2.num);<span style="font-family:微软雅黑;">//调用的是父类的成员变量</span>
}
}</span>
运行结果:4 3
多态中成员函数的特点:(非静态)
编译时:参考引用型变量所属的类中是否有调用的函数。有,编译通过。没有,编译失败。
运行时:参考的是对象所属的类中是否有调用的函数。
简单说:编译看左边,运行看右边。
<span style="font-size:12px;">class Fu{
void show(){
System.out.println("fu show");
}
}
class Zi extends Fu{
void show(){
System.out.println("zi show");
}
}
class Test{
public static void main(String[] args) {
Fu c=new Zi();
c.show();
}
}</span>
运行结果:
多态中成员函数的特点:(静态)
编译时:参考的是对象所属的类中是否有调用的函数。
运行时:参考的是对象所属的类中是否有调用的函数。
简单说:编译和运行看左边。
class Fu{
static void method(){
System.out.println("fu static method");
}
}
class Zi extends Fu{
static void method(){
System.out.println("zi static method");
}
}
class Test{
public static void main(String[] args) {
Fu c=new Zi();
c.method();//这里调用的是父类的静态方法
Fu.method();//静态方法是类方法,对象还没有时就已经存在于方法区
}
}
运行结果:
4 内部类
内部类:将一个类定义在另一个类的里面,里面那个类就称为内部类(内置类,嵌套类)
访问特点:
a、内部类可以直接访问外部类的成员(包括私有)
b、外部类访问内部类成员必须创建内部类对象
<span style="font-size:12px;">class Outer{
private int num = 3;
class Inner
{
void show(){
System.out.println("Inner run..." + num);
}
}
}
class Test{
public static void main(String[] args){
//直接访问外部类中的内部类中的成员
Outer.Inner in = new Outer().new Inner();
in.show();
}
}</span>
P.S.
1、内部类定义在成员位置上,可以被private、static成员修饰符修饰。被static修饰的内部类只能访问外部类中的静态成员。
2、静态的内部类,内部类成员也是静态的,可以不用创建内部类对象,直接调用。
<span style="font-size:12px;">class Outer{
private static int num = 3;
static class Inner
{
static void show(){
System.out.println("Inner run..." + num);
}
}
}
class Test{
public static void main(String[] args){
//如果内部类是静态的,相当于一个外部类
Outer.Inner in = new Outer.Inner();
in.show();
Outer.Inner.show();//内部类为静态的可以直接通过类名访问
}
}</span>
P.S.
如果内部类中定义了静态成员,该内部类也必须是静态的!
<span style="font-size:12px;">class Outer{
int num = 3;
class Inner{
int num = 4;
void show(){
int num = 5;
System.out.println(num);
System.out.println(this.num);
System.out.println(Outer.this.num);
}
}
void method(){
new Inner().show();
}
}
class Test{
public static void main(String[] args){
new Outer().method();
}
}</span>
运行结果 :5 4 3
内部类定义在局部位置上,也可以直接访问外部类中的成员;同时可以访问所在局部中的局部变量,但必须是被final修饰的。
<span style="font-size:12px;">class Outer{
int num = 3;
void method(final int y){
final int x = 9;
class Inner{
void show(){
System.out.println("show..." + x + "," + y);
}
}
Inner in = new Inner();
in.show();
}
}
class Test{
public static void main(String[] args){
new Outer().method(4);
}
}</span>
【注意】内部类定义了静态变量,内部类必须是静态的。
【注意】静态是成员修饰符,不能修饰局部变量。
内部类定义在局部
1、不可以被成员修饰符修饰如:static
2、可以直接访问外部类中的成员,因为还持有外部类中的引用,但是不可以访问他所在的局部中的变量,只能访问被final修饰的局部变量
【】匿名内部类
内部类必须继承一个类或者实现接口
abstract class Demo//匿名内部类需要继承一个类或者实现接口
{
abstract void show();
}
class Outer
{
int num = 3;
/*class Inner extends Demo//一般定义内部类方法
{
void show()
{
System.out.println("show....."+Outer.this.num);
}
}*/
void method(){
//new Inner().show()
//匿名内部类,将内部类(注释掉的代码块)简写
new Demo()
{
void show()
{
System.out.println("show....."+Outer.this.num);
}
}.show();
}
}
class Test{
public static void main(String[] args){
new Outer().method();
}
}
内部类格式:new 父类或者接口(){定义子类的功能}------------(实质为一个匿名子类对象 ,因此上面的方法只能对方法也只能调用一次)
abstract class Demo//匿名内部类需要继承一个类或者实现接口
{
abstract void show1();
abstract void show2();
}
class Outer
{
int num = 3;
/*class Inner extends Demo
{
void show()
{
System.out.println("show....."+Outer.this.num);
}
}*/
void method(){
//new Inner().show()
//匿名内部类,将内部类(注释掉的代码块)简写
Demo d=new Demo()//当父类有多个方法时可以定义一个父类引用指向子类对象,实现多个方法调用
{
void show1()
{
System.out.println("show.....1..."+Outer.this.num);
}
void show2()
{
System.out.println("show.....2..."+Outer.this.num);
}
void show3()
{
System.out.println("show.....3..."+Outer.this.num);
}
};
d.show1();
d.show2();
//d.show3();会报错,不能调用匿名对象的特有方法,因为父类中没有定义此方法无法调用
}
}
class Test{
public static void main(String[] args){
new Outer().method();
}
}
运行结果
【例】
interface Inter//匿名内部类需要继承一个类或者实现接口
{
void method();
}
class Outer
{
int num = 3;
/*static class Inner implements Inter
{
public void method()
{
System.out.println("method run");
}
}*/
public static Inter function()
{
//return new Inner();
return new Inter()//匿名内部类
{
public void method()
{
System.out.println("method run");
}
};
}
}
class Test{
public static void main(String[] args){
//Outer.function() 说明Outer中有一个静态方法是function
//.method(); function的返回值是对象,而且是Inter类型的对象
Outer.function().method();
}
}
【例】
interface Inter{
void show1();
void show2();
}
/*
通常的使用场景之一:
当函数参数是接口类型时,而且接口中的方法不超过三个。
可以用匿名内部类作为实际参数进行传递。
*/
class InnerClassDemo{
public static void main(String[] args){
show(new Inter(){
public void show1(){
System.out.println("...show1..." );
}
public void show2(){
System.out.println("...show2..." );
}
});
}
public static void show(Inter in){
in.show1();
in.show2();
}
}
【例】
class Test{
public static void main(String[] args){
new Object()//创建Object的子类匿名对象
{
void show()
{
System.out.println("object run");
}
}.show();
}
}