第一讲 封装
一、对象的封装
1、概念
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
2、 好处
a)将变化隔离。
b)便于使用。
c)提高重用性。
d)调高安全性。
3、 原则
a) 将不需要对外提供的内容都隐藏起来。
b) 把属性都隐藏,提供公共方法对其访问。
4、 封装的表现形式之一——private(私有)
private关键字:权限修饰符;用于修饰类中的成员(成员变量,成员函数);私有只在本类中有效。
常用之一:
将成员变量私有化,对外提供对应的set,get方法对其进行访问。提高对数据访问的安全性。
如:我有一个人对象,而年龄这一属性我不想被对象访问,我就可以将其设为private。
2、定义一个类封装它的name,age属性
class Person
{
private Person(){}
private String name = "tsz";
private int age;
private static String country = "cn";
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void speak()
{
System.out.println(this.name+"..."+this.age);
}
public static void showCountry()
{
System.out.println("country="+Person.country);//静态的可以类名直接调用
Person.method();
}
public static void method()
{
System.out.println("method run");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person("zhangsan",20);
p.setName("lisi");
new Person();
}
}
/*
Person p = new Person("zhangsan",20);
该句话都做了什么事情?
1,因为new用到了Person.class.所以会先找到Person.class文件并加载到内存中。
2,执行该类中的static代码块,如果有的话,给Person.class类进行初始化。
3,在堆内存中开辟空间,分配内存地址。
4,在堆内存中建立对象的特有属性。并进行默认初始化。
5,对属性进行显示初始化。
6,对对象进行构造代码块初始化。
7,对对象进行对应的构造函数初始化。
8,将内存地址付给栈内存中的p变量。
*/
3、注意
private :私有,权限修饰符:用于修饰类中的成员(成员变量,成员函数)。
私有只在本类中有效。
将name私有化以后,类以外即使建立了对象也不能直接访问。
但是人应该有年龄,就需要在Person类中提供对应访问age的方式。
*注意:私有仅仅是封装的一种表现形式。
之所以对外提供访问方式,就因为可以在访问方式中加入逻辑判断等语句。
对访问的数据进行操作。提高代码健壮性。
二、构造函数的详细解读
1、构造函数
1、 特点:
a) 函数名与类名相同。
b) 不用定义返回值类型。
c) 不可以写return语句。
2、 作用:
给对象进行初始化。
3、构造函数的小细节:
当一个类中没有定义构造函数时,那么系统就会默认给该类加入一个空参数的构造函数。当在类中自定义了构造函数后,默认的构造函数就没有了。
4、构造函数和一般函数在写法上有不同。
在运行上也有不同:
构造函数式在对象一建立就运行,给对象初始化。而一般方法是对象调用才执行,给是对象添加对象具备的功能。一个对象建立,构造函数只运行一次。而一般方法可以被该对象调用多次。
5、什么时候定义构造函数?
当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中。
6、构造代码块
作用:给对象进行初始化。对象一建立就运行,而且优先于构造函数运行。
和构造函数的区别:
构造代码块是给所有对象进行初始化。
而构造函数是给对应的对象初始化。
构造代码块中定义的是不同对象共性的初始化内容。
2、构造函数实现
class Person
{
private String name;
private int age;
/*
构造代码块。
作用:给对象进行初始化。
对象一建立就运行,而且优先于构造函数执行。
和构造函数的区别:
构造代码块是给所有对象进行统一初始化,
而构造函数是给对应的对象初始化。
构造代码快中定义的是不同对象共性的初始化内容。
*/
{
//System.out.println("person code run");//构造代码块
cry();
}
Person()/*构造函数*/
{
System.out.println("A: name="+name+",,age="+age);
}
/*构造函数*/
Person(String n)
{
name = n;
System.out.println("B: name="+name+",,age="+age);
//cry();
}
/*
public void setName(String n)
{
name = n;
}
public String getName()
{
return name;
}
*/
Person(String n,int a)
{
name = n;
age = a;
System.out.println("C: name="+name+",,age="+age);
}
public void cry()
{
System.out.println("cry......");
}
}
class PersonDemo2
{
public static void main(String[] args)
{
Person p1 = new Person();
Person p2 = new Person("lisi");
}
}
第二讲 继承
一、继承(extends)
1、继承概述
(1)继承的体系结构:就是对要描述的事物进行不断的向上抽取,就出现了体系结构。
要了解这个体系结构中最共性的内容,就看最顶层的类。
要使用这个体系的功能,就用最底层的类创建对象。
(2)继承的好处:
A:继承的出现,提高了代码的复用性。
B:继承的出现,让类与类之间产生了关系,extends来表示,这个关系的出现,为后面我们讲面向对象的第三个特点
多态打下了基础。
(3)特点
A:java只支持单继承(其实确切的说是java对多继承进行了优化,避免了安全问题)。
B:java支持多重(层)继承。
(4)注意:
A:子类可以直接访问父类中的非私有的属性和行为。
B:不要仅为了获取其他类中部分功能而去继承。
C:类与类之间要有所属( " is a " )关系,xx1是xx2的一种。
如何判断A和B是否有继承关系?
A如果继承B,那么就可以说A是B的一种。
2、继承的实例
举例:将学生和工人的共性描述提取出来,单独进行描述,只要让学生和工人与单独描述的这个类有关系,就可以了。
class Person
{
String name;
int age;
}
class Student extends Person
{
void study()
{
System.out.println("good study");
}
}
class Worker extends Person
{
void work()
{
System.out.println("good work");
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
Student s = new Student();
s.name = "zhagnsan";
}
}
3、继承后子父类之间成员的关系
(1)成员变量
class Fu{
int num = 5;
}
class Zi extends Fu{
int num = 20;
public void show() {
int num = 30;
System.out.println("num:"+num);
//当局部变量和成员变量重名的时候用this来区分
System.out.println("this num:"+this.num);
//当子类和父类出现了同名变量,用super来区分
System.out.println("father num:"+super.num);
}
}
总结:在一个类中如果方法中的局部变量和方法外的成员变量重名,那么如果在方法内输出这变量,就是方法自己的变量里的值,
想要区分要用this,加上this.就是输出成员变量的值
在子父类中如果出现成员变量重名的时候,在子类输出会输出自己的变量里的值,想要区分要用super,加上super.就是输出
父类里变量的值
this和super的区分:
**this代表本类对象的引用
super本类对象父类的引用。
**this可以用于区分局部变量和成员变量同名的情况。
super可以用于区分子类和父类成员变量同名的情况。
**一般,子类中不会出现和父类同名的成员变量。面试可能问到。
(2)成员方法
class Fu {
public void show() {
System.out.println("fu show");
}
public void method() {}
}
class Zi extends Fu{
public void show(){
System.out.println("zi show");
}
}
子类中存在和父类成员方法同名的这种现象,叫做重写,复写,覆盖。
重写(override)和重载(overload)的区别:
重载的特点:
**在同一类中。
**方法名相同,参数列表不同。
重写的特点:
**要有继承关系。在子父类中
**方法的声明相同。(方法名和参数列表都相同)
***覆盖时,子类方法权限一定要大于等于父类方法权限
父类的权限不能是私有的
***静态只能覆盖静态。
(3)构造方法
class Fu{
Fu(){}
Fu(int age){
System.out.println("father age:"+age);
}
}
class Zi extends Fu{
Zi(){
this(40);
System.out.println("son");
}
Zi(int age){
super();
System.out.println("son age:"+age);
}
}
Zi z = new Zi();
Zi z = new Zi(30);
总结:子类中所有的构造方法默认都会访问父类中空参数的构造方法。
**因为每一个构造方法的第一行都有一条默认的语句super();
当父类中没有空参数的构造方法时,子类的构造函数必须通过this
或者super语句指定要访问的构造方法。或者手动提供无参构造方法。
this(...):调用本类中的构造方法
super(...):调用父类中的构造方法
构造方法用于创建对象,并进行初始化.建议如果你写了有参的构造函数,也要把空参的构造函数再手动加上
否则你定义了有参的构造函数,空参的系统就不会再给了
你这样创建对象的时候就会报错Person p = new Person();//这句话是会去找空参的构造函数
class Person{
Person(){}
Person(int age){
this.age = age;
}
Person(int age,String name){
this(age);
//this.age = age;
this.name = name;
}
}
//Person p =new Person(); //系统默认给出无参构造
//当你手动给出构造方法后,系统就不会再给出默认的空的构造方法。
手动无参数,如果你想给属性赋值或者做一些初始化,无参你别删不就行了吗。
class Demo{
private String name;
Demo(){}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
4、final可以用来修饰什么呢?
(1)final可以用来修饰类:被fainl修饰的类不能被继承。
(2)final可以用来修饰成员方法:被final修饰的成员方法不能被重写。
(3)final可以用来修饰变量:被final修饰的变量为常量,值不能被修改。
(4)被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,有可以修饰局部变量。
当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读。
而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有字母都大写,如果由多个单词组成。
单词间通过_连接。
(5)内部类定义在类中的局部位置上是,只能访问该局部被final修饰的局部变量。
常量的命名规范:要求大写。
final double PI = 3.14;
final修饰的变量可以在声明的时候直接赋值,还可以在构造方法可以给final修饰的变量赋值。
实例:
class Demo
{
final int x = 3;
public static final double PI = 3.14;
final void show1()
{}
void show2()
{
final int y = 4;
System.out.println(3.14);
}
}
class SubDemo extends Demo
{
//void show1(){}
}
class FinalDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
第三讲 多态
1、多态的概述
多态:可以理解为事物存在的多种体现形态。
(1)某一类事物的多种存在形态。
方法重载(静态多态)
方法重写(动态多态,对象多态)
(2)对象多态的前提
A:类与类(或接口)要有继承(或实现)关系。
B:一定要有方法的重写(覆盖)。
C:一定要有父类或者接口的引用指向子类的对象。
(3)多态的好处
多态的出现大大的提高程序的扩展性。
(4)多态的弊端:
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
2、多态的基本运用
abstract class Animal
{
abstract void eat();
}
class Cat extends Animal
{
public void eat()
{
System.out.println("吃鱼");
}
public void catchMouse()
{
System.out.println("抓老鼠");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("吃骨头");
}
public void kanJia()
{
System.out.println("看家");
}
}
class Pig extends Animal
{
public void eat()
{
System.out.println("饲料");
}
public void gongDi()
{
System.out.println("拱地");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
//Cat c = new Cat();//创建cat对象
//c.eat();
/*
Cat c1 = new Cat();
function(c1);//函数的参数是animal的子类对象
function(new Dog());
function(new Pig());
*/
//Animal c = new Cat();
//c.eat();
function(new Cat());//直接传匿名对象
}
public static void function(Animal a)//Animal a = new Cat();
{
a.eat();
//a.catchMouse();
}
/*
public static void function(Cat c)//
{
c.eat();
}
public static void function(Dog d)
{
d.eat();
}
public static void function(Pig p)
{
p.eat();
}
*/
}
3、继承重写是多态的一种表现
class Fu
{
static int num = 5;
void method1()
{
System.out.println("fu method_1");
}
void method2()
{
System.out.println("fu method_2");
}
static void method4()
{
System.out.println("fu method_4");
}
}
class Zi extends Fu
{
static int num = 8;
void method1()
{
System.out.println("zi method_1");
}
void method3()
{
System.out.println("zi method_3");
}
static void method4()
{
System.out.println("zi method_4");
}
}
class DuoTaiDemo4
{
public static void main(String[] args)
{
//以左边为主
// Fu f = new Zi();
//
// System.out.println(f.num);//结果是父类的num=5
//
// Zi z = new Zi();
// System.out.println(z.num);
//f.method1();
//f.method2();
//f.method3();
Fu f = new Zi();
System.out.println(f.num);//父类的num
f.method4();//静态 的情况都要看左边,如果非静态看右边运行Zi
Zi z = new Zi();
z.method4();
}
}
4、多态中成员的特点
Fu f = new Zi()
A、非静态方法
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。
B、成员变量:编译和运行都看Fu
C:静态方法:编译和运行都看Fu。
举例: 动物的例子: 向上转型 Animal a = new Cat(); a.eat(); //a.catchMouse();
向下转型
Cat c = (Cat)a;
c.eat();
c.catchMouse();
//向上转型
Animal a = new Dog();
//向下转型 转换异常
//Cat c = (Cat)a;
Dog d = (Dog)a;
总结:无论是向上转型还是向下转型,变化的都是子类对象,绝对不能把父类对象强转为子类类型