1、 面向对象基本概念
一、 什么是面向对象:
- 面向对象是一种编程思想。
- 面向对象是一种思考问题的思维方式。
二、 建立面向对象思维方式:
- 先整体, 再局部
- 先抽象, 再具体
- 能做什么, 再怎么做
三、 如何学习面向对象:
- 掌握一门面向对象语言的语法
- 熟悉面向对象的设计原则
- 熟悉面向对象设计模式
2、 类与对象
一、什么是类
- 类是: 分类、 类别。
- 通过分类, 我们可以区别不同的事物种类, 在日常生活当中, 我们常常这样做。
- 所以, 类是一组具有相同特性(属性) 与行为(方法) 的事物集合
二、类与对象的关系
类表示一个共性的产物, 是一个综合的特征, 而对象, 是一个个性的产物, 是一个个体的特征。
类由属性和方法组成:
- 属性: 就相当于一个个的特征
- 方法: 就相当于人的一个个的行为, 例如: 说话、 吃饭、 唱歌、 睡觉
3、 类和对象的定义格式
在Java中可以使用以下的语句定义一个类:
class 类名称{
属性名称;
返回值类型 方法名称(){
}
}
对象的定义:
一个类要想真正的进行操作, 则必须依靠对象, 对象的定义格式如下:
类名称 对象名称 = new 类名称() ;
按照以上的格式就可以产生对象了。
如果要想访问类中的属性或方法(方法的定义),
则可以依靠以下的语法形式:
访问类中的属性:
对象.属性 ;
调用类中的方法:
对象.方法() ;
在java中对象声明有两种含义
声明对象:
Horse horse= null;
// 表示声明了一个对象, 但是此对象无法使用, horse没有具体的内存指向
实例化对象:
horse= new Horse() ;
// 表示实例化了对象, 可以使用
通过对象调用方法:
horse.eat()
匿名对象调用方法:
new Horse().eat()
4、对象内存分析
- new 关键字表示创建一个对象
- new 关键字表示实例化对象
- new 关键字表示申请内存空间
注意: 如果使用一个没有申请内存空间的对象, 会报空指针异常:
java.lang.NullPointerException
对象在内存中的结构:
给对象的属性赋值:
horse.name=”小白”; horse.age=4;
在内存中创建多个对象:
Horse horse1=null; horse1=new Horse();
Horse horse2=null; horse2=new Horse();
分别给两个对象的属性赋值:
horse1.name=”小白”; horse2.name=”小黑”;
horse1.age=4; horse2.age=5;
声明两个对象, 一个实例化, 一个没实例化
Horse horse1=null; horse1=new Horse();
Horse horse2=null;
对象之间的赋值
horse1.name=”小白”; horse2=horse1;
horse1.age=4; horse2.name=”小黑”;
horse1.name=”小白”; horse2=horse1;
horse1.age=4; horse2.name=”黑黑”;
horse2.name=”小黑”;
类与对象小结
- new关键字: 表示向内存申请空间, 也表示实例化一个对象, 创建一个对象。
- 一个对象在内存中的大小, 由该对象的所有属性所占的内存大小的总和。 引用类型变量在32位系统上占4个字节, 在64位系统上占8个字节。 加上而外的对象隐性数据所占的大。
- 相同的类型才可以赋值
- 不同的引用, 指向同一个对象, 任何一个引用改变对象的值, 其它引用都会反映出来。
- 编程时要注意的问题, 在确定不使用对象时, 要尽早释放对象: 引用=null
- 当一个堆中的对象没有被任何引用变量所指向时, 该对象会被JVM 的 GC 程序认为是垃圾对象, 从而被回收。
5、 封装性
一、 封装性的概念
- 封装性是面向对象思想的三大特征之一。
- 封装就是隐藏实现细节, 仅对外提供访问接口。
封装有:
属性的封装、 方法的封装、 类的封装、 组件的封装、 模块化封装、 系统级封装…
二、 封装的好处
- 模块化
- 信息隐藏
- 代码重用
- 插件化易于调试
- 具有安全性
三、封装缺点:
会影响执行效率
封装之前:
class Person{
String name;
int age;
}
封装之后:
class Person{
//属性是成员变量
private String name;
private int age;
//参数及方法内定义的变量是局部变量
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
成员变量和局部变量
1、 在类中的位置不同
- 成员变量: 在类中定义
- 局部变量: 在方法中定义或者方法的参数
2、 在内存中的位置不同
- 成员变量: 在堆内存(成员变量属于对象, 对象进堆内存)
- 局部变量: 在栈内存(局部变量属于方法, 方法进栈内存)
3、 生命周期不同
- 成员变量: 随着对象的创建而存在, 随着对象的销毁而消失
- 局部变量: 随着方法的调用而存在, 随着方法的调用完毕而消失
4、 初始化值不同
- 成员变量: 有默认初始化值, 引用类型默认为null
- 局部变量: 没有默认初始化值, 必须定义, 赋值, 然后才能使用
注意:
局部变量名称可以和成员变量名称一样, 在方法中使用的时候, 采用的是就近原则。
6、 构造方法
什么是构造方法
- 构造方法就是类构造对象时调用的方法, 用于对象的初始化工作
- 构造方法是实例化一个类的对象时, 也就是new 的时候, 最先调用的方法。
构造方法的定义:
构造方法是在类中定义的, 构造方法的定义格式: 方法名称与类名称相同, 无返回值类型的声明。
对象的实例化语法:
Dog dog = new Dog();
//new Dog后面有个括号, 带括号表示调用了方法, 此时调用的方法就是构造方法了
构造方法重载:
无参构造方法:
public Dog(){}
带一个参数的构造方法:
public Dog(String name){
this.name = name;
}
带多个参数的构造方法:
public Dog(String name,int age){
this.name = name;
this.age = age;
}
构造方法小结
- 构造方法名称与类名相同, 没有返回值声明(包括 void)
- 构造方法用于初始化数据(属性)
- 每一个类中都会有一个默认的无参的构造方法
- 如果类中有显示的构造方法, 那么默认构造方法将无效
- 如果有显示的构造方法, 还想保留默认构造 方法, 需要显示的写出来。
- 构造方法可以有多个, 但参数不一样, 称为构造方法的重载
- 在构造方法中调用另一个构造方法, 使用this(…), 该句代码必须在第一句。
- 构造方法之间的调用, 必须要有出口。
- 给对象初始化数据可以使用构造方法或setter方法, 通常情况下, 两者都会保留。
- 一个好的编程习惯是要保留默认的构造方法。 (为了方便一些框架代码使用反射来创建对象)
- private Dog(){}, 构造方法私有化, 当我们的需求是为了 保正该类只有一个对象时。
- 什么时候一个类只需要一个对象? 比如, 工具类(没有属性的类, 只有行为) 并且该工具对象被频繁使用。
- 权衡只用一个对象与产生多个对象的内存使用, 来确定该类是否要定义为只需要一个对象。
7、 this关键字
在Java基础中, this关键字是一个最重要的概念。 使用this关键字可以完成以下的操作:
- 调用类中的属性
- 调用类中的方法或构造方法
- 表示当前对象
/**
this关键字
调用类中的属性
· 调用类中的方法或构造方法
· 表示当前对象:在方法被调用的过程中,哪个对象调用了方法,在方法内的this就表示谁
类名.this
*/
public class Test4{
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){ //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("我是"+getName()+",我爱吃鱼");
System.out.println("我是"+this.name+",我爱吃鱼");
/*
我是咪咪,我爱吃鱼
我是咪咪,我爱吃鱼
我是咪咪,我爱吃鱼
*/
}
}
8、 值传递与引用传递
示例1:值传递
public class Test19 {
public static void main(String[] args){
int x = 10;
method(x) ;
System.out.println("x="+x) ; //x=10
}
public static void method(int mx){
mx = 20;
}
}
示例2:引用传递
public class Test19 {
public static void main(String[] args){
Duck d = new Duck();
method(d) ;
System.out.println("Duck age ="+d.age) ;//Duck age =5
}
public static void method(Duck duck){
duck.age = 5;
}
}
class Duck{
int age = 2; //省略封装
}
示例3:String传递
public class Test19 {
public static void main(String[] args){
String name = "小飞";
method(name) ;
System.out.println("name="+name) ;//name=小飞
}
public static void method(String sname){
sname = "小备";
}
}
示例4: String传递
public class Test19 {
public static void main(String[] args){
Person p = new Person();
method(p) ;
System.out.println("person name= "+p.name) ;//person name= 备备
}
public static void method(Person p ){
p.name = "备备";
}
}
class Person{
String name = "飞飞"; //省略封装
}
9、 对象的一对一关系
两个对象之间的一对一关系:
比如:一个英雄(Hero) 对一个兵器(Weapon)
/**
两个对象之间的一对一关系:
比如:一个英雄(Hero)对一个兵器(Weapon)
双向一对一
单向一对一
*/
public class Test5{
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;
}
}
10、 static关键字
static关键字的作用:
- 使用static关键字修饰一个属性
声明为static的变量实质上就是全局变量 - 使用static关键字修饰一个方法
通常, 在一个类中定义一个方法为static, 那就是说, 无需本类的对象即可调用此方法 - 使用static关键字修饰一个类(内部类)
class文件加载到内存的结构图:
声明为static的方法有以下几条限制:
- 它们仅能调用其他的static 方法。
- 它们只能访问static数据。
- 它们不能以任何方式引用this 或super。
什么时候使用static?
所有对象共同的属性或方法, 那么我们可以定义为静态的。
/**
static 关键字
1、静态变量或方法不属于对象,依赖类
2、静态变量是全局变量,生命周期从类被加载后一直到程序结束
3、静态变量只有存一份,在静态方法区中存储
4、静态变量是本类所有对象共享一份
5、建议不要使用对象名去调用静态数据,直接使用类名调用
6、static修饰一个方法,那么该方法属于类,不属于对象,直接用类名调用
7、静态方法不能访问非静态属性和方法,只能访问静态。
*/
public class Test6{
public static void main(String[] args){
System.out.println(args.length);
for(String s: args){
System.out.println(s);
}
//Role beibei = new Role("刘备","蜀国");
//Role yunchang = new Role("云长","蜀国");
//Role feifei = new Role("张飞","蜀国");
Role beibei = new Role("刘备");
Role yunchang = new Role("云长");
Role feifei = new Role("张飞");
System.out.println(beibei.getInfo());
System.out.println(yunchang.getInfo());
System.out.println(feifei.getInfo());
System.out.println(beibei.country);
System.out.println(yunchang.country);
System.out.println(feifei.country);
beibei.country = "魏国";//尽量不用对象调用静态变量
System.out.println("------------------");
System.out.println(beibei.country);
System.out.println(yunchang.country);
System.out.println(feifei.country);
System.out.println("------------------");
//用类名调用静态变量
System.out.println(Role.country);
System.out.println("------------------");
Role.setCountry("吴国");
System.out.println(beibei.country);
}
}
//角色
class Role{
private String name;
static String country = "蜀国";//静态变量(全局变量)
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 static void setCountry(String country){
Role.country = country;
}
/*
public void setCountry(String country){
this.country = country;
}
public String getCountry(){
return country;
}
*/
public String getInfo(){
return "name="+name+",country="+country;
}
}
/*
0
name=刘备,country=蜀国
name=云长,country=蜀国
name=张飞,country=蜀国
蜀国
蜀国
蜀国
------------------
魏国
魏国
魏国
------------------
魏国
------------------
吴国
*/
11、 main方法分析
主方法:
public static void main(String[] args){
//代码块
}
- public: 公有的, 最大的访问权限
- static: 静态的, 无需创建对象
- void:: 表示没有返回值, 无需向JVM返回结果
- main: 方法名, 固定的方法名
- String[] args: 表示参数为字符串数组, 可以在调用方法时传入参数
12、代码块
1、 普通代码块
直接写在方法中的代码块就是普通代码块
示例:
public class Demo1{
public static void main(String []args){
{// 普通代码块
String info = “局部变量-1” ;
System.out.println(info) ;
}
String info = “局部变量-2” ;
System.out.println(info) ;
}
}
2、 构造块是在类中定义的代码块
示例:
class Demo{
{ // 构造块
System.out.println("构造块") ;
}
public Demo(){
System.out.println("构造方法。 ") ;
}
}
3、 在类中使用static声明的代码块称为静态代码块
示例:
class Demo{
{
System.out.println("构造块") ;
}
static{
System.out.println("静态代码块") ;
}
public Demo(){
System.out.println("构造方法。 ") ;
}
}
4、 同步代码块(多线程中讲解)
13、 单例设计模式
单例设计模式: 保证一个类仅有一个实例, 并提供一个访问它的全局访问点。
- 构造方法私有化
- 声明一个本类对象
- 给外部提供一个静态方法获取对象实例
在项目中为什么要使用单例,单例有什么好处?
- 在设计一些工具类的时候(通常工具类,只有功能方法,没有属性)
- 工具类可能会被频繁调用
- 目的是为了节省重复创建对象所带来的内存消耗,从而来提高效率
两种实现方式:
- 饿汉式
- 懒汉式
public class Test19 {
public static void main(String[] args){
HungryMan hungryMan=HungryMan.getInstance();
System.out.println(hungryMan.getClass().toString());//class HungryMan
LazyMan lazyMan=LazyMan.getInstance();
System.out.println(lazyMan.getClass().toString());//class LazyMan
}
}
//单例模式,构造方法私有化
//饿汉式单例:一上来就构造方法,缺点是容易占用过多内存
class HungryMan{
private HungryMan(){}
private static HungryMan hungryMan = new HungryMan();
public static HungryMan getInstance(){
return hungryMan;
}
}
//懒汉式单例:需要的时候再构造
class LazyMan{
private LazyMan(){}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan==null){
lazyMan=new LazyMan();
}
return lazyMan;
}
}
//双重检测锁模式 懒汉单例
class DCLLazyMan{
private DCLLazyMan(){}
private volatile static DCLLazyMan lazyMan;
public static DCLLazyMan getInstance(){
if(lazyMan==null){
synchronized (DCLLazyMan.class){
if(lazyMan==null){
lazyMan=new DCLLazyMan(); //不是一个原子性操作
/*
1、会分配一个内存空间
2、执行构造方法,初始化对象
3、把这个对象指向这个空间
*/
}
}
}
return lazyMan;
}
}
//静态内部类实现
class Holder{
private Holder(){}
public static Holder getInstance(){
return InnerClass.holder;
}
public static class InnerClass{
private static final Holder holder=new Holder();
}
}
14、 对象数组与管理
对象数组就是数组里的每个元素都是类的对象, 赋值时先定义对象, 然后将对象直接赋给数组。
示例:
Chicken[] cs= new Chicken[10];
使用对象数组实现多个Chicken的管理。
数组的动态管理,里面用到了计数器:count,很有意思。
但要实现单纯的增加和删除,可以查看上一节。
/**
对象数组与管理
使用对象数组实现多个Chicken的管理。
动态数组:
1、数组是一种线性数据结构
2、数组不适合作删除插入等操作,适合添加,查找,遍历
*/
import java.util.Arrays;
public class Test9{
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,"小红",2));
cm.add(new Chicken(5,"小奋",4));
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(6);
c.print();
System.out.println("-------update----------");
cm.update(new Chicken(1,"下蛋公鸡",20));
cm.printAll();
}
}
//小鸡管理类
class ChickenManager{
private Chicken[] cs = null;
private 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[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();
}
}
}
//小鸡类(数据对象) value object (VO)
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="+name+",age="+age);
}
}
/*
数组的长度是:10
-------printAll----------
id=1,name=小小,age=10
id=2,name=小二,age=8
id=3,name=小三,age=6
id=4,name=小红,age=2
id=5,name=小奋,age=4
id=6,name=小斗,age=1
-------find----------
id=6,name=小斗,age=1
-------update----------
id=1,name=下蛋公鸡,age=20
id=2,name=小二,age=8
id=3,name=小三,age=6
id=4,name=小红,age=2
id=5,name=小奋,age=4
id=6,name=小斗,age=1
Process finished with exit code 0
*/