2020/8/20
面向对象基本概念
一、什么是面向对象:
- 面向对象是一种编程思想。
- 面向对象是一种思考问题的思维方式。
二、建立面向对象思维方式:
- 先整体,再局部
- 先抽象,再具体
- 能做什么,再怎么做
三、如何学习面向对象:
1.掌握一门面向对象语言的语法
2、熟悉面向对象的设计模式
3、熟悉面向对象设计模式
类与对象:
1、类是分类、类别。类是一组具有相同特性(属性)与行为(方法)的事务集合。
2、类表示一个共性的产物,是一个综合的特征,而对象,是一个个性的产物,是一个个体的特征。
3、类是由属性和方法组成:属性就相当于一个个的特征,方法就相当于人的一个个行为,例如:说话、吃饭、唱歌、睡觉。
类和对象的定义格式
在java中对象声明有两种含义:
声明对象:Horse horse=null;
//表示声明了一个对象,但是此对象无法使用,horse没有具体的内存指向
实例化对象:horse=new Horse( );
//表示实例化对象,可以使用
//通过对象调用方法
horse.eat()
//匿名对象调用方法
new Horse().eat()
【例子】
package HeadFirst;
/**
*/
public class Test14 {
public static void main(String[] args) {
Horse h=null;//声明一个类的变量(除了八种基本数据类型外,都是引用数据类型,包括数组)
//创建一个Horse类型对象,实例对象
h=new Horse();
//有了对象,我们就可以调用对象的属性和方法
h.name="赤兔马";
h.age=350;
h.run();//调用方法,那么方法就会被执行
//匿名对象只能使用一次,用完后该对象就会被释放
new Horse().run();
h=null;//把对象释放
//h.eat();//当对象不存在时,调用该对象的属性和方法将报错(空指针)
}
}
//自定义一个类(类型)
class Horse{
//类中定义属性(特征)
String name;//字符串是引用类型变量,不是基本数据类型
int age;
public void run(){
System.out.println("我是"+name+",我"+age+"了");
}
}
类与对象小结
1、new关键字:表示向内存申请空间,也表示实例化一个对象,创建一个对象。
2、一个对象在内存中的大小,由该对象的所有属性所占的内存大小的总和。引用类型变量在32位系统上占4个字节,在64位系统上占8个字节
3、相同的类型才可以赋值
4、不同的引用,指向同一个对象,任何一个引用改变对象的值,其他引用都会反映出来
5、 编程时要注意的问题,在确定不使用对象时,要尽早释放对象:引用=null
6、当一个堆中的对象没有任何引用变量所指向时,该对象就会被JVM的GC程序认为是垃圾对象,从而被回收
面向对象封装性
成员变量和局部变量
1、在类中的位置不同
成员变量:在类中定义
局部变量:在方法中定义或方法的参数
2、在内存中的位置不同
成员变量:在堆内存(成员变量属于对象,对象进堆内存)
局部变量:在栈内存(局部变量属于方法,方法进栈内存)
3、生命周期不同
成员变量:随着对象的创建存在,随着对象的销毁而消失
局部变量:随着方法的调用存在,随着方法的调用完毕而消失
4、初始化值不同
成员变量:有默认初始化值,引用类型默认为null
局部变量:没有默认初始化值,必须定义,赋值,然后才能使用
注意:局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则
【例子】
package HeadFirst;
/*
封装性
如果属性没有封装,那么在本类之外创建对象后,可直接访问属性
private关键字:访问权限修饰符,public表示公有的,private表示私有的,私有的属性和方法,只能在本类中访问
公有的属性和方法,可以被类外部的其他类访问,想要在类外部访问私有属性,我们需要提供公有的方法来间接访问
通常在一个类中,属性都私有化,并对外提供setter and getter
*/
public class Test15 {
public static void main(String[] args) {
Person p1=new Person();
// p1.name="菲菲";
// p1.age=18;
p1.setName("郎朗");
p1.setAge(18);
}
}
//没有封装之前
class Person{
//属性的封装
private String name;
private int age;
//setter and getter
//对外提供一个为name属性设置的方法
public void setName(String name){
this.name=name;
}
//对外提供一个获取name值的方法
public String getName(){
return name;
}
public void setAge(int age){
//局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则
this.age=age;//这里局部变量age与成员变量age一样,若不加this就会根据就近原则把age赋给局部变量自身,报错
}
public int getAge(){
return age;
}
public void run(int len){//参数也是局部变量
int dis=len;//m也是局部变量
System.out.println("我跑了"+dis+"米");
// int i;
// System.out.println(i);//会报错,尚未初始化i,所以局部变量定义后需要赋值,不会有默认值
}
}
构造方法
构造方法小结
(1)构造方法名称与类名相同,没有返回值(包括void)
(2)构造方法用于初始化数据(属性)
(3)每一个类中都会有一个默认的无参的构造方法
(4)如果类中有显示的构造方法,那么默认的构造方法将无效
(5)如果有显示的构造方法,还想保留默认构造方法,需要显示的写出来
(6)构造方法可以有多个,但参数不一样,称为构造方法重载
(7)在构造方法中调用另一个构造方法,使用this(…),该句代码必须在第一句
(8)构造方法之间的调用,必须要有出口
(9)给对象初始化数据可以使用构造方法或setter方法,通常情况下,两者都会保留
(10)一个好的编程习惯是保留默认的构造方法。(为了方便一些框架代码使用反射来创建对象)
(11)private Dog( ){},构造方法私有化,当我们的需求是为了保证类只有一个对象时。
什么时候一个类只需要一个对象?比如,工具类(没有属性的类,只有行为)并且该工具对象被频繁使用。权衡只用一个对象与产生多个对象的内存使用,来确定该类是否要定义为只需要一个对象。
【代码】
package HeadFirst;
/*
构造方法:构造方法是在类中定义的,构造方法的定义格式:方法名称与类名称相同,无返回值类型的声明
1构造方法就是类构造对象时调用的方法,用于对象的初始化工作
2构造方法就是实例化一个类的对象时,也就是new的时候,最先调用的方法
*/
public class Test16 {
public static void main(String[] args) {
Dog dog=new Dog();//不带参数的构造函数
Dog dog1=new Dog("wang wang",1);//带两个参数的构造函数
}
}
class Dog{
public Dog(){
System.out.println("构造方法执行了");
}
public Dog(String name){
this.name=name;
System.out.println("带一个参数的构造方法执行了");
}
public Dog(String name,int age){
this(name);//调用其他构造方法时,此语句需要在第一句,在构造方法相互调用时必须要有出口
// this.name=name;
this.age=age;
System.out.println("带两个参数的构造方法执行了");
}
private String name;
private int age;
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}
}
this 关键字
【例子】
package HeadFirst;
/*
this关键字
调用类中的属性
调用类中的方法或构造方法
表示当前对象:在方法被调用过程中,哪个对象调用了方法,在方法内的this就表示谁
类名.this
*/
public class Test17 {
public static void main(String[] args) {
Cat cat=new Cat();
cat.setName("咪咪");
cat.setAge(3);
cat.eat();
}
}
class Cat{
private String name;
private int age;
public void setName(String name){
this.name=name;//this代表当前对象
}
public String getName(){
return name;
}
public void setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}
public void eat(){
//在方法中使用this调用类中的其他方法或属性,this可省略,this前面可以使用当前的类名.this
System.out.println("我是"+Cat.this.getName()+",我爱吃鱼");
System.out.println("我是"+this.name+",我爱吃鱼");
}
}
值传递与引用传递
【例子】
package HeadFirst;
//值传递
public class ValueDemo {
public static void main(String[] args) {
int x=10;
method(x);
System.out.println("x="+x);//所以输出还是10
}
public static void method(int mx){
mx=20;//改的是mx不是x
}
}
package HeadFirst;
//引用传递
public class RefDemo1 {
public static void main(String[] args) {
Duck d=new Duck();
method(d);//d里面存的是地址,将d的地址赋给duck
System.out.println("Duck age="+d.age);//输出是5
}
public static void method(Duck duck){
duck.age=5;
}
}
class Duck{
int age=2;//省略封装
}
package HeadFirst;
//String传递
//字符串本身就是一个对象
public class RefDemo2 {
public static void main(String[] args) {
String name="小飞";//一个对象
method(name);//name中存的是地址,将name地址赋给sname,sname也指向对象“小飞”
System.out.println("name="+name);
}
public static void method(String sname){
sname="小备";//这又是一个对象,使得sname指向对象“小备”,
}
}
package HeadFirst;
//String传递
public class RefDemo3 {
public static void main(String[] args) {
Person1 p= new Person1();
method(p);
System.out.println("Person1 name="+p.name);
}
public static void method(Person1 per){
per.name="备备";
}
}
class Person1{
String name="飞飞";//省略封装
}
对象的一对一关系
【例子】
package HeadFirst;
public class Test18 {
public static void main(String[] args) {
Hero hero=new Hero("刘备",300);
Weapon weapon=new Weapon("双股剑",3);
//把两个对象关联起来
hero.setWeapon(weapon);
weapon.setHero(hero);
//通过英雄来获取他的信息
String name=hero.getName();
int age=hero.getAge();
Weapon w=hero.getWeapon();
System.out.println("我是"+name+",我"+age+"岁,我的武器是:"+w.getName()+",排行"+w.getGrade()+"级");
}
}
//英雄类
class Hero{
private String name;
private int age;
private Weapon weapon;//一对一关系
public void setWeapon(Weapon weapon){
this.weapon=weapon;
}
public Weapon getWeapon() {
return weapon;
}
public Hero(){}
public Hero(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 setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}
}
//兵器类
class Weapon{
private String name;
private int grade;
private Hero hero;
public void setHero(Hero hero){
this.hero=hero;
}
public Hero getHero(){
return hero;
}
public Weapon(){}
public Weapon(String name,int grade){
this.name=name;
this.grade=grade;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setGrade(int grade){
this.grade=grade;
}
public int getGrade(){
return grade;
}
}
#我是刘备,我300岁,我的武器是:双股剑,排行3级
static 关键字
static关键字作用:
1、使用static关键字修饰一个属性
声明为static的变量实质上就是全局变量
2、使用static关键字修饰一个方法
通常,在一个类中定义一个方法为static,那就是说,无需本类对象即可调用此方法
3、使用static关键字修饰一个类(内部类)
【例子】
package HeadFirst;
/*
static关键字
1静态变量或方法不属于对象,依赖类
2静态变量是全局变量,生命周期从类被加载后一直到程序结束
3静态变量只有存一份,在静态方法区中存储
4静态变量是本类所有对象共享一份
5建议不要使用对象名去调用静态变量,直接使用类名调用
6static修饰一个方法,那么该方法属于类,不属于对象,直接使用类名调用
*/
public class Test19 {
public static void main(String[] args) {
//Role bei=new Role("刘备","蜀国");
//Role fei=new Role("张飞","蜀国");
//Role yu=new Role("关羽","蜀国");
Role bei = new Role("刘备");
Role fei = new Role("张飞");
Role yu = new Role("关羽");
System.out.println(bei.getInfo());
System.out.println(fei.getInfo());
System.out.println(yu.getInfo());
System.out.println("-------------------");
System.out.println(bei.country);//去掉country前面的private
System.out.println(fei.country);
System.out.println(yu.country);
System.out.println("-------------------");
bei.country = "秦国";
System.out.println(bei.country);
System.out.println(fei.country);
System.out.println(yu.country);
System.out.println("-------------------");
System.out.println(Role.country);
}
}
//角色
class Role {
private String name;
// private String country;
static String country="蜀国";//将country定义成静态变量,(去掉前面的private)
// public Role(String name,String country){
// this.name=name;
// this.country=country;
// }
public Role(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
/*
public void setCountry(String country) {
this.country = country;
}
public String getCountry() {
return country;
}
*/
public String getInfo() {
return "name:" + name + ",country:" + country;
}
}
#name:刘备,country:蜀国
#name:张飞,country:蜀国
#name:关羽,country:蜀国
#-------------------
#蜀国
#蜀国
#蜀国
#-------------------
#秦国
#秦国
#秦国
#-------------------
#秦国
【注意】
声明为static的方法有以下几条限制:
它们仅能调用其他的static方法
它们只能访问static数据
它们不能以任何方式引用this或super
什么时候使用static?
所有对象共同的属性或方法,那么我们应该定义为静态的
main方法分析
主方法:
public static void main(String [] args){
//代码块
}
public:公有的,最大的访问权限
static:静态的,无需创建对象
void:表示没有返回值,无需向JVM返回结果
main:方法名,固定的方法名
String[] args:表示参数为字符串数组,可以在调用方法时传入参数
【代码】
package HeadFirst;
/*
代码块:
1普通代码块:在方法中写的代码块
2构造块:是在类中定义的代码块,在创建对象时被调用,优于构造方法执行
3在类中使用static声明的代码块称为静态代码块
在第一次使用的时候被调用(创建对象),只会执行一次,优于构造块执行
我们在项目开发中,通常会使用静态代码块来初始化只调用一次的数据
小结:重点会使用的顺序是静态代码块,普通代码块,同步代码块,构造代码块
*/
public class Test20 {
public static void main(String[] args) {
Student s=new Student();
Student s1=new Student();
}
}
class Student{
public Student(){
System.out.println("构造方法");
}
static {
System.out.println("我是静态构造块");
}
{
System.out.println("我是构造代码块");
}
public void study(){
//限制作用域
{
int i=10;
System.out.println("我是普通代码块");
}
}
}
#我是静态构造块
#我是构造代码块
#构造方法
#我是构造代码块
#构造方法
单例设计模式
单例设计模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点
1构造方法私有化
2声明一个本类对象
3给外部提供一个静态方法获取对象实例
两种实现方式:
1饿汉式
2懒汉式
【例子】
package HeadFirst;
/*
单例设计模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点
1构造方法私有化
2声明一个本类对象
3给外部提供一个静态方法获取对象实例
两种实现方式:
1饿汉式:在第一次调用getInstance方法时,对象被创建,到程序结束后释放
2懒汉式:在类被加载后,对象被创建,到程序结束后释放
在项目中为什么要使用单例,单例有什么好处?
1在设计一些工具类的时候(通常工具类,只有功能方法,没有属性)
2工具类可能会被频繁调用
目的是为了节省重复创建对象所带来的内存消耗,从而来提高效率
能不能使用构造方法私有化+静态方法来替换单例?
*/
public class Test21 {
public static void main(String[] args) {
//Singleton1 s=new Singleton1();//构造方法私有化,故会出错
Singleton2 s = Singleton2.getInstance();//故通过静态方法获取对象实例
s.print();
Singleton2 s2 = Singleton2.getInstance();
s2.print();
System.out.println(s==s2);//结果为true说明这两个对象是一样的,因为对象是静态的
}
}
//使用构造方法私有化+静态方法来实现工具类,比如Math
class Tools{
private Tools(){};
public static void print1(){
}
public static void print2(){
}
}
//饿汉式:占用内存时间长,提高效率
class Singleton1 {
private Singleton1() {
}//构造方法私有化
private static Singleton1 s = new Singleton1();//声明一个本类对象,对象是静态的说明只会执行一次
public static Singleton1 getInstance() {//给外部提供一个静态方法获取对象实例
return s;
}
public void print() {
System.out.println("测试方法");
}
}
//懒汉式:占用内存时间短,效率稍低(程序里多了个判断)
class Singleton2{
private Singleton2(){
}
private static Singleton2 s;
public static Singleton2 getInstance(){
if(s==null){
s=new Singleton2();
}
return s;
}
public void print(){
System.out.println("测试方法2");
}
}
对象数组与管理
【例子】
package HeadFirst;
import java.util.Arrays;
/*
对象数组与管理
使用对象数组实现多个Chicken的管理
动态数组:
1数组是一种线性数据结构
2数组不适合作删除插入等操作,适合添加,查找,遍历
*/
public class Test22 {
public static void main(String[] args) {
ChickenManager cm = new ChickenManager(5);
cm.add(new Chicken(1, "一", 10));
cm.add(new Chicken(2, "二", 8));
cm.add(new Chicken(3, "三", 6));
cm.add(new Chicken(4, "四", 4));
cm.add(new Chicken(5, "五", 2));
cm.add(new Chicken(6, "六", 1));
System.out.println("数组的长度是:" + cm.length());
System.out.println("----------printAll-----------");
cm.printAll();
System.out.println("-----------find-----------");
Chicken c = cm.find(5);
c.print();
System.out.println("----------update------------");
cm.update(new Chicken(1,"母鸡",20));
cm.printAll();
System.out.println("----------delete------------");
cm.delete(3);
cm.printAll();
}
}
//小鸡管理类
class ChickenManager {
private Chicken[] cs = null;
int count = 0;//记录当前数组元素的个数
public ChickenManager(int size) {
if (size > 0) {
cs = new Chicken[size];
} else {
cs = new Chicken[5];
}
}
public int length() {
return cs.length;
}
//添加:实现动态数组
public void add(Chicken c) {
if (count >= cs.length) {//数组已满,需要扩充
//算法1:扩充原来数组大小的一半,cs.length*3/2+1
//算法2:扩充原来数组的一倍,cs.length*2
int newLen = cs.length * 2;
cs = Arrays.copyOf(cs, newLen);//将原先cs中的元素挨个复制到新扩充的数组中
} else {
cs[count] = c;
count++;
}
}
//删除
public void delete(int id) {
for(int i=0;i<count;i++){
if(cs[i].getId()==id){
//找到了要删除的对象,把该对象之后的对象向前移动一位
for(int j=i;j<count-1;j++){
cs[j]=cs[j+1];
}
//把最后一个对象赋值为空(删除)
cs[count-1]=null;
count--;//
break;
}
}
}
//更新
public void update(Chicken c) {
Chicken temp=find(c.getId());
if(temp!=null){
temp.setName(c.getName());
temp.setAge(c.getAge());
}
}
//查找
public Chicken find(int id){
for (int i = 0; i < count; i++) {
if (cs[i].getId() == id) {
return cs[i];
}
}
return null;
}
//输出所有
public void printAll() {
for (int i = 0; i < count; i++) {
cs[i].print();
}
}
}
//小鸡类
class Chicken {
private int id;
private String name;
private int age;
public Chicken() {
}//一般情况下最好保留默认的构造方法
public Chicken(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void print() {
System.out.println("id=" + id + ",name=" + ",age" + age);
}
}
数组的长度是:10
----------printAll-----------
id=1,name=,age10
id=2,name=,age8
id=3,name=,age6
id=4,name=,age4
id=5,name=,age2
-----------find-----------
id=5,name=,age2
----------update------------
id=1,name=,age20
id=2,name=,age8
id=3,name=,age6
id=4,name=,age4
id=5,name=,age2
----------delete------------
id=1,name=,age20
id=2,name=,age8
id=4,name=,age4
id=5,name=,age2