黑马程序员-面向对象1
-------android培训java培训期待与您交流!----------
1.面向对象概念
a.面向对象概念
- 理解面向对象:面向对象是面向过程而言。是一种思想。将功能封装进对象,强调具备了功能的对象。
- 任何事物都能抽象成对象,比如人。人都有共性:年龄,性别,身高,学历等等。把人抽象成对象,而这个人对象内部的功能可以定义成年龄,性别,身高等等这些;而对于外界想要调用你这个人的某个功能属性,对他来说不必关心你人对象内部的实现,他只管调用你的这个对象某个功能,获取他需要的结果即可。方便快捷,提高编程效率。
- 面向对象三个特征:封装、继承、多态
b.类与对象的关系
- 类:对现实生活中事物的描述。
- 对象:就是实实在在的个体的这类事物。
- 定义:
A. 定义类就是在描述事物,也就是在定义属性和行为。属性和行为共同成为类中的成员(成员变量,成员函数)。
B. 对象由new操作符来生成,在堆内存产生一个实体。 - 示例:
//定义类
class People{
//属性
public int age;
public string name;
...
//定义方法 行为
public int age()
{
return age;
}
//对象
People p = new People();
p.age();
...
- 成员变量和局部变量
A.最大区别区别:作用的范围不一样。成员变量作用于整个类中,从属于类,不必初始化。局部变量作用于函数中或者语句中,必须手动初始化。
B.内存的位置:成员变量在堆内存中,因为对象的存在才存在于内存中。局部变量存在栈内存中。 - 匿名对象
A.匿名对象是对象的简化形式,没有名字的对象。格式为new 实例名称();
B.匿名对象两种使用情况:
(1).当对对象方法仅进行一次调用时
(2).匿名对象可以作为实际参数进行传递
new People().age = 23;
age(new People());
c.封装
- OOP三大特征之一。
- 概念:指隐藏独享的属性和实现细节,仅对外提供公共访问方式。
- 封装的好处:将变化隔离、便于使用、提高重用性、提高安全性
- 封装原则:将不需要对外提供的内容都隐藏起来、把属性都隐藏,提供公共方法对其访问。
- private:私有权限反问修饰符。用于修饰类中的成员(成员变量,成员函数),私有指只在本类中有效。仅仅是封装的一种表现形式。
d.构造函数
- 特点:函数名与类名相同、不用定义返回值类型、不可以写return语句。
- 作用:给对象初始化,用于对象初始化。
- 注意:当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造函数,用于对象初始化。当在类中定义了初始化构造函数,默认的构造函数就没有了。
- 特点:构造函数和一般函数在写法上不同
、
在运行上也不同,构造函数是在对象一建立就运行,给对象初始化。而一般方法是对象调用才执行,给是对象添加对象具备的功能、
一个对象建立,构造函数值运行一次,而一般方法可以被该对象调用多次。
//定义类
class People{
//默认构造函数
People(){}
}
- 定义构造函数的时间:当对象需要一定的特征或者行为,就需要对其进行构造函数。
构造代码块:
//定义类
class People{
/**
* 作用:给对象进行初始化
* 特点:对象一建立就运行,而且优先于构造函数执行,针对所有的对象进行初始化都会先被执行一次。
* 和构造函数的区别:
* 构造代码块是给所有对象进行统一初始化。
* 构造函数时给对应的对象进行初始化。
* 用处:定义给不同对象的共性进行初始化。
*/
{
System.out.println("构造代码块");
}
}
e.this关键字
- 用于区分局部变量和成员变量同名情况。
- this代表它所在的函数所属对象的引用。
- this代表本类对象。
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
- 应用:当定义类中功能时,该函数内部要用到调用该函数的对象是,这时使用this来代表使用此对象。凡是本类功能内部使用了本类对象,都用this表示。
class People{
private int age;
/**
* 构造函数
* @param age
*/
People(int age)
{
this.age = age;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
/**
* 比较两个人的年龄是否相等。此处的this代表当前调用的对象的引用也就是p1。
* @param p 人的对象变量
* @return boolean
*/
public boolean compare(People p)
{
return this.age == age;
}
public static void main(String[] args) {
People p1 = new People(20);
People p2 = new People(23);
boolean b = p1.compare(p2);
System.out.println("b = " + b);
}
}
- this与构造函数:this用与构造函数的之间互相调用。而且只能定义在构造函数的第一行。因为初始化要先执行
class People{
private int age;
private String name;
/**
* 空构造函数
*/
People()
{
}
/**
* 构造函数
* @param age
*/
People(int age)
{
this();//调用People()构造函数
}
/**
* 构造函数
* @param age
* @param name
*/
People(int age, String name)
{
this(23);//调用People(int age)构造函数
this.name = name;
}
f.static关键字
- static修饰符,修饰变量、方法和代码块。
- 当成员被static修饰后,可以用类名直接调用。格式:类名.静态成员。
static String name;
public static void main(String[] args) {
System.out.println("People.name = " + People.name);
}
- 特点:随着类的加载而加载,静态随着类的消失而消失,生命周期最长、优先于对象的存在、被所有对象所共享、可以直接被类名所调用。
- 实例变量和静态成员变量区别:
A.存放位置:(1)静态成员变量随着类的加载而存在方法区中。(2)实例变量随着对象的建立而存在于堆内存中。
B.生命周期:(1)静态成员变量最长,随着类的消失而消失。(2)实例变量随着对象的消失而消失。 - 静态使用注意点:
A.静态方法只能访问静态成员(方法与变量)。非静态方法即可以访问静态也可以访问非静态。
B.静态方法中不可以定义this、super关键字。因为静态优先于对象存在。
C.主函数是静态的。 - 静态优缺点:
A.优:对对象的共享数据进行单独空间的存储,节省空间。可以直接被类名调用。
B.缺:生命周期过长、访问出现局限性(只能访问静态)。
静态的使用时间:
- 静态变量(类变量)定义:当对象中的出现对象调用的共享数据时,该数据被静态所修饰存在于方法区中;对象中的特有数据定义成非静态存在于堆内存中。
- 静态函数定义:当类中的某个功能没有访问到非静态数据(对象特有的数据),那么该功能可以定义成静态的(static)。
class People{
String name;
//没有关联到非成员变量name,那么这个函数可以设置成static的,方便后面直接通过类名调用
public static void show()
{
System.out.println("没有访问到成员变量");
}
public static void main(String[] args) {
//调用此方法时,与name没有关系。没有封装进数据。可以不建立对象来调用,设置成static的通过类名调用方便明了。
People.show();
}
}
静态代码块
- 格式:
static
{
System.out.println("静态代码块");
}
- 特点:随着类的加载而执行,只执行一次,用于给类进行初始化。并优先于主函数。
g.mian函数
public static void main(String[] args)
- public:表示全局所有访问权限最大。也就是封装性。
- static:表示只能通过类调用。主函数随着类的加载就已经存在了。
- void:主函数没有具体返回值。
- main:不是关键字,特殊的一个词,可以被JVM识别。
- String[] args:函数的参数,是一个字符串数组。
主函数是固定格式:JVM识别
h.对象初始化过程:
People p = new People("zhangsan", 23);
1.因为new用到了People.class,所以会先找到People.class文件并加载到内存中。
2.执行该类中的static代码块,如果有的话。再给People.class类进行初始化。
3.在堆内存中开辟空间,分配内存地址。
4.在堆内存中建立对象的特有属性,并进行默认初始化。
5.对属性进行显示初始化。
6.对对象进行构造代码块初始化。
7.对对象进行对应的构造函数初始化。
8.将内存地址赋给内存中的引用变量p。
i.继承
- 面向对象特征之二。
- 把对象间的共性特征进行抽取,类与类之间有关系,最后抽象成一个共性类,被抽取的类的继承自这个共性类就可。减少代码量,提高复用性。
- 形式:
继承类 extends 被继承类
- Java只支持单继承。支持多层继承:B继承A,D继承B。D类可以使用A类的功能。
- 继承关系中类成员的特点:
A. 变量:如果子类中出现非私有的同名变量时,子类要访问本类中的变量,用this。子类要访问父类中的同名变量,用super,super跟this的使用几乎一致。super代表父类对象的引用,this代表本类对象引用。
/**
* Created by Sergio on 2014/11/2.
*/
public class ExtendsDemo1 {
public static void main(String[] args) {
Sun s = new Sun();
s.show();
}
}
class Parents{
int num = 5;
}
//子继承父类
class Sun extends Parents{
int num = 4;
void show()
{
System.out.println(num);//访问的是子类的变量,相当于this.num
System.out.println(super.num);//碰到同名变量可以使用super关键字访问父类的变量。
}
}
B. 函数:当父类出现和子类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容,如同父类的函数被覆盖掉。这也叫做覆盖(重写)。
/**
* Created by Sergio on 2014/11/2.
*/
public class ExtendsDemo1 {
public static void main(String[] args) {
Sun s = new Sun();
s.show();
}
}
class Parents{
int num = 5;
void show()
{
System.out.println("被覆盖");
}
}
//子继承父类
class Sun extends Parents{
int num = 4;
void show()
{
System.out.println(num);//访问的是子类的变量,相当于this.num
System.out.println(super.num);//碰到同名变量可以使用super关键字访问父类的变量。
}
}
覆盖注意点:子类覆盖父类,必须保证子类权限大于等于父类权限,才可以叫覆盖,否则编译失败。静态只能覆盖静态。
C. 构造函数:在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句super()[会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();];
/**
* Created by Sergio on 2014/11/2.
*/
public class ExtendsDemo2 {
public static void main(String[] args) {
Sun2 s2 = new Sun2(2);
System.out.println(s2.x);//访问的是父类抽取的x = 4;
}
}
class Parents2{
int x = 4;
Parents2()
{
System.out.println("fu run");
}
Parents2(int x)
{
System.out.println("x = " + x);
}
}
//子继承父类
class Sun2 extends Parents2{
Sun2()
{
//super();隐式的构造函数
super(1);//根据父类构造函数来指定
System.out.println("zi run");
}
Sun2(int x)
{
super(3);//根据父类构造函数来指定
System.out.println("zi run");
}
}
super语句一定定义在子类构造函数的第一行,初始化动作要先做。子类的所有的构造函数,默认都会访问父类中空参数的构造函数,因为子类每一个构造函数的第一行都有一句隐式super();当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问的构造函数。子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。子类中至少会有一个构造函数会访问父类中的构造函数。
j. final关键字
- 解释:最终。修饰符。
- 修饰的范围:类、函数、变量。
- 特点:被final修饰的类不可以被继承,为了避免被继承,被子类复写功能。
- final类:被final修饰的类不可以复写。
- final变量:被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,又可以修饰局部变量。
- 使用的时候。描述事物时,一些数据的出现值是固定的。可以用一个常量来表示。
- 命名:常量的书写规范所有字母都大写。如果有多个单词定义,每个单词通过"_"链接。
P_AI
。比如PI。
k. 抽象类
- 多个类出现相同功能,但是功能主体不同,进行向上抽取,只抽取功能定义,而不抽取功能主题。
- 定义方式:
abstract 类名
包含抽象方法和非抽象方法、abstract 方法名();
抽象方法只有方法名没有方法内容。 - 注意:抽象方法只存在于抽象类中。抽象类不可以用new创建对象,因为调用抽象方法没有意义。抽象方法要被使用,必须由子类复写所有的抽象方法后,建立子类对象调用;如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类,因为抽象方法只存在于抽象类中。
- 抽象类不可以实例化,没意义。
l. 接口
- 格式:
interface 接口名{}
。接口中定义常量、方法固定修饰符如下(修饰符可以省略,interface会自动加上。):
A. 常量:public static final
B. 方法:public abstract
接口中的成员都是public。接口中的方法都是抽象的。
/**
* Created by Sergio on 2014/11/3.
*/
interface InterfaceDemo {
public static final int NUM = 3;//变量定义
public abstract void intefaceDemo();//方法定义
}
//接口必须被子类实现用关键字implements,接口可以被多个子类实现。子类也可以继承于一个父类。必须将接口中的所有方法实现因为接口中的方法都是抽象的
class InterfaceTest extends ExtendsDemo2 implements InterfaceDemo,InterfaceDemo2....{
@Override
public void intefaceDemo() {
}
}
接口是不可以创建对象的,因为有抽象方法存在。需要被子类实现,覆盖接口中的抽象方法后,子类才可以实例化,否则子类是一个抽象类。
接口与接口之间的关系是继承关系,支持多继承关系。
- 接口特点:
-. 接口是对外暴露的规则
-. 接口是程序的功能扩展
-. 接口可以用来多实现
-. 类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口
-. 接口与接口之间可以有继承关系
m. 多态与关键字instanceof
一. 概念
- 面向对象特征之三
- 概念:事物存在的多种体现形态。比如人分为:男人、女人。人 r = new 男人(); 人 r2 = new 女人();
二. 多态扩展性
- 多态的体现:父类的引用指向了自己的子类对象(父类)。
- 多态的前提:类与类之间有关系,有继承或者实现。
- 多态的益处:多态的出现提高了程序的扩展性,通常有前提条件是:存在覆盖。
- 多态的弊端:只能使用父类的引用访问父类中的成员。
- 多态中成员的特点:
- 非静态成员函数:
a. 在编译时期:参与引用型变量所属的类中是否有调用的方法,如果有编译通过,如果没有编译失败。
b. 在运行时期:参与对象所属的类中是否有调用的方法。
总结就是:成员函数在多态调用时,编译看左边,运行看右边。 - 成员变量:
无论编译和运行:都参考左边(引用型变量所属的类) - 静态成员函数(子类不覆盖父类方法,父类走父类,子类走子类。变量也一样。):
无论编译和运行:都参考左边
- 非静态成员函数:
/**
* Created by Sergio on 2014/11/19.
*/
public class PolymorphicDemo {
public static void main(String[] args) {
//调用子类覆盖父类的eat()方法,有局限性在这点上
function(new Dog());
function(new Pig());
}
//定义共性的功能方法eat()
public static void function(Animal a)
{
a.eat();
}
}
/**
* 集中抽象功能的父类Animal
*/
abstract class Animal{
abstract void eat();
}
/**
* 定义Dog类继承之Animal父类,并且拥有自己的特定功能类houseKeeping().
*/
class Dog extends Animal{
//覆盖父类eat()方法
public void eat()
{
System.out.println("Dog.eat");
}
//子类特有的方法
public void houseKeeping()
{
System.out.println("看家");
}
}
/**
* 定义Pig类继承之Animal父类,并且拥有自己的特定功能类fat().
*/
class Pig extends Animal{
//覆盖父类eat()方法
public void eat()
{
System.out.println("Pig.eat");
}
//子类特有的方法
public void fat()
{
System.out.println("养膘");
}
}
三. 父类、子类类型转型
/**
* Created by Sergio on 2014/11/19.
*/
public class PolymorphicDemo {
public static void main(String[] args) {
//类型提升,向上转型。将Dog类型提升为Animal类型
Animal a = new Dog();
a.eat();
/**
* 如果要调用子类特有的方法操作:强制将父类的引用转成子类类型,向下转型.将父类a引用强制转换成子类类型Dog。
*
* 注意:不能将父类对象转成子类类型。能转换的是父类指向了自己的子类对象时,该引用可以被提升也可以强制转换。
* 多态始终都是子类在作者变化。
*/
Dog d = (Dog)a;
d.houseKeeping();
//fun函数调用子类对象的功能
fun(new Dog());
fun(new Pig());
}
//提炼共性函数特征fun(Animal a)
public static void fun(Animal a)
{
a.eat();
/**
* 判断传递进来的子类是那个对象的。使用关键字instanceof,判断左边对象是否属于右边类的实例,返回boolean
* 类型数据。
*
* instanceof是一个二元操作符。使用的前提:1.子类型有限、2.对象需要做比较首先判断是否属于某个类的实例
*/
if (a instanceof Dog){
Dog d = (Dog)a;
d.houseKeeping();
}
else if(a instanceof Pig)
{
Pig p = (Pig)a;
p.fat();
}
}
}
/**
* 集中抽象功能的父类Animal
*/
abstract class Animal{
abstract void eat();
}
/**
* 定义Dog类继承之Animal父类,并且拥有自己的特定功能类houseKeeping().
*/
class Dog extends Animal{
//覆盖父类eat()方法
public void eat()
{
System.out.println("Dog.eat");
}
//子类特有的方法
public void houseKeeping()
{
System.out.println("看家");
}
}
/**
* 定义Pig类继承之Animal父类,并且拥有自己的特定功能类fat().
*/
class Pig extends Animal{
//覆盖父类eat()方法
public void eat()
{
System.out.println("Pig.eat");
}
//子类特有的方法
public void fat()
{
System.out.println("养膘");
}
}
四:小示例
package com.sergio.lianxi;
/**
* Created by Sergio on 2014/11/20.
*
* 需求:电脑运行实例。电脑运行基于主板。
*/
public class PolymorphicDemo2 {
public static void main(String[] args) {
MotherBoard mb = new MotherBoard();
mb.run();
mb.usePCI(new SoundCard());
}
}
/**
* 电脑的各种部件基于电脑主板上的pci接口运行。模拟PCI接口。
*/
interface PCI{
public void open();
public void close();
}
//定义主板,运行的基础
class MotherBoard{
public void run(){
System.out.println("主板运行");
}
public void usePCI(PCI p)//接口型引用指向自己的子类对象
{
if(p != null)
{
p.open();
p.close();
}
}
}
/**
* 需要运行部件实现方式
*/
class SoundCard implements PCI
{
@Override
public void open() {
System.out.println("声卡运行");
}
@Override
public void close() {
System.out.println("声卡关闭");
}
}
2.设计模式
单例设计模式
- 设计模式概念:解决某一类问题最行之有效的方法,可以重复利用。java有23种设计模式。
- 单例模式:只对外提供一个实例。在内存中只存在一个对象。
- 三要素:(1)在类体中需要具有静态的私有的本类型的变量、(2)构造方法必须是私有的,不允许实例化、(3)提供一个公共的静态的入口方法。
package com.sergio.lianxi;
/**
* 单例模式饿汉式。
* 第一种方式。类一进内存就建立对象。
* Created by Sergio on 2014/10/10.
*/
public class SinglePattern {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "SinglePattern{" +
"age=" + age +
'}';
}
//以下三个方法是单例使用的必须写法
/**
* 静态私有化本类的成员变量
*/
private static SinglePattern instance = new SinglePattern();
/**
* 私有化构造方法,禁止对象实例化
*/
private SinglePattern(){}
/**
* 对外提供一个公共的静态入口方法
*/
public static SinglePattern getInstance()
{
return instance;
}
}
class SinglePatternTest
{
public static void main(String[] args) {
SinglePattern s1 = SinglePattern.getInstance();
SinglePattern s2 = SinglePattern.getInstance();
s1.setAge(23);
System.out.println("s2 = " + s2);
}
}
package com.sergio.lianxi;
/**
* Created by Sergio on 2014/10/10.
* 单例模式懒汉式。也叫延迟加载式。只有调用了getInstance方法时,才建立对象
*/
public class SinglePattern2 {
private static SinglePattern2 instance = null;
private SinglePattern2(){}
public static synchronized SinglePattern2 getInstance()
{
if
if(instance == null)
{
instance = new SinglePattern2();
}
return instance;
}
}
注意:开发时候使用饿汉式,也就是第一种。