一、类与对象的关系
-
类 抽象的概念
-
对象 看得见摸得着的具体实体
类实例化后得到对象 -
创建宠物猫类
package com.alan.animal;
/**
* 宠物猫类
* @author alan
*
*/
public class Cat {
//属性:昵称、年龄、体重、品种
String name; //昵称 String 默认值null
int month; //年龄 int 默认值为 0
double weight; //体重 double 默认值为0.0
String species; //品种
//方法:跑动、吃东西
//跑动的方法
public void run() {
System.out.println("小猫快跑");
}
//对跑动的方法进行重载
public void run(String name) {
System.out.println(name+"快跑");
}
//吃东西的方法
public void eat() {
System.out.println("小猫吃鱼");
}
}
- 宠物猫类测试
package com.alan.animal;
public class CatTest {
public static void main(String[] args) {
//对象实例化
Cat one = new Cat();
//测试 调用方法
one.eat();
one.run();
one.name = "花花";
//在类没有初始化的情况下,构造函数对其进行默认的赋值 String 为null ,int 为0,double为0.0
System.out.println("name="+one.name);
System.out.println("species="+one.species);
System.out.println("month="+one.month);
System.out.println("weight="+one.weight);
one.run(one.name);
}
}
单一责任原则
- 一个类只负责一个功能,如果一个类功能太多则耦合性过高,复用的可能性越低。
实例化对象
分为两个部分:
- 声明对象 Cat one 在栈中开辟一块空间,但是没有赋值为null
- 实例化对象 new cat(); 在堆里面开辟的一块空间
Cat one = new cat(); // 将堆空间中的地址赋值给栈,栈为指向堆的引用
image.png
构造方法(构造函数)
在new的时候构造方法会被调用,不能被对象单独调用
1、构造方法与类同名且没有返回值。
2、只能在对象实例化的时候调用。
3、当没有指定构造方法时,系统会自动添加无参的构造方法。
4、就近原则,如果使用name=name赋值,会优先选择同一个作用域,进行赋值,所以要使用不同名字,或者用this.name=name 进行赋值。
this关键字
-
代表当前对象的属性或者方法,例如this.name表示当前这个class的属性。
image.png
image.png
this与创建的对象具有相同的ID值,说明this调用的就是当前的对象
this();可以调用无参构造方法,但是一定是在其他构造方法的第一行。
其他的方法不能调用构造函数。
二、Java封装
将类的某些信息隐藏在类内部,不允许外部程序直接访问。
image.png
- 只有get方法 是只读属性
- 只有set方法 是只写属性
创建包
同一个包中不允许出现相同文件名的Cat,引用同名的
package com.alan.test;
import com.alan.animal.Cat;
import com.alan.mechanics.*; //通过.*直接加载到直接的类,不能使用com.alan.*这种方式
public class Test {
public static void main(String[] args) {
com.alan.mechanics.Cat one = new com.alan.mechanics.Cat(); //直接导入的是animal中的Cat,如果需要用mechanics中的需要在代码中加入com等具体的包路径
// System.out.println("调用成功!");
}
}
static关键字
//static 静态 静态成员 类成员 ,所有对象都使用一块内存空间,所以所有的实例化对象,static修饰的值都相同
public static int price; //用static修饰后 price变为斜体
1、类对象共享该内存信息
2、类加载时产生,直到类销毁的时候释放。生命周期长。
3、静态成员的访问方式,既可以通过对象名访问,也可以通过类名访问。
one.price (对象名访问) Cat.price(类名访问)
4、当static修饰方法时,该方法为静态方法,推荐采用类名调用,如Cat.eat();
5、有静态的属性和方法,静态方法中不能直接访问同一个类的非静态成员,只能直接调用同一个类中的静态成员。只能通过实例化后进行访问没有静态的类,没有静态的局部变量。
代码块{}
- 普通代码块:在方法中的代码块叫做普通代码块。顺序执行,先出现,先执行。
- 构造代码块:在类中的代码块叫构造代码块。创建对象时调用,优先与构造方法执行。
- 静态代码块:构造代码块加上static关键字,就是静态代码块。类加载时调用,优先于构造代码块执行。 无论实例化多少个对象,只执行一次。
command+shift+f 行内容过长,换行快捷键。
三、Java封装综合案例
- 在方法中通过对象作为参数,传递的是它的引用,可以通过引用获取该对象的所有信息。
- 数组未实例化会造成空指针异常 java.lang.NullPointerException
package com.alan.model;
/**
* 学生类
*
* @author alan
*
*/
public class Student {
// 成员属性:学号、姓名、性别、年龄、专业对象
private String studentNo;
private String studentName;
private String studentSex;
private int studentAge;
private Subject studentSubject;
// 无参构造
public Student() {
}
// 带参构造
public Student(String studentNo, String studentName, String studentSex, int studentAge,Subject studentSubject) {
this.setStudentNo(studentNo);
this.setStudentName(studentName);
this.setStudentSex(studentSex);
this.setStudentAge(studentAge);
this.setStudentSubject(studentSubject);
}
/**
* 获取专业对象,如果没有实例化,先实例化后再返回
* @return 专业对象
*/
public Subject getStudentSubject() {
if(this.studentSubject == null)
this.studentSubject = new Subject(); //无参构造的重要性就提现出来了
return studentSubject;
}
public void setStudentSubject(Subject studentSubject) {
this.studentSubject = studentSubject;
}
public String getStudentNo() {
return studentNo;
}
public void setStudentNo(String studentNo) {
this.studentNo = studentNo;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentSex() {
return studentSex;
}
public void setStudentSex(String studentSex) {
this.studentSex = studentSex;
}
public int getStudentAge() {
return studentAge;
}
/**
* 给年龄赋值。限定大于10,小于50,否则赋值为18
*
* @param studentAge 传入的年龄
*/
public void setStudentAge(int studentAge) {
if (studentAge < 10 || studentAge > 50)
this.setStudentAge(18);
else
this.studentAge = studentAge;
}
/**
* 学生自我介绍的方法
*
* @return 自我介绍信息,包括姓名、学号、性别、年龄
*
*/
public String introduction() {
String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n学号:" + this.getStudentNo() + "\n性别:"
+ this.getStudentSex() + "\n年龄:" + this.getStudentAge()+ "\n所报专业名称:" + this.getStudentSubject().getSubjectName() + "\n学制年限:"
+ this.getStudentSubject().getSubjectLife();
return str;
}
/**
* 学生自我介绍的方法
*
* @param subjectName 所学专业名称
* @param subjectLife 学制年限
* @return 自我介绍信息,包括姓名、学号、性别、年龄、所学专业名称、学制年限
*/
public String introduction(String subjectName, int subjectLife) {
String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n学号:" + this.getStudentNo() + "\n性别:"
+ this.getStudentSex() + "\n年龄:" + this.getStudentAge() + "\n所报专业名称:" + subjectName + "\n学制年限:"
+ subjectLife;
return str;
}
/**
* 学生自我介绍的方法
* @param sub1 传入学科对象
* @return 自我介绍信息,包括姓名、学号、性别、年龄、所学专业名称、学制年限
*/
public String introduction(Subject sub1) {
String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n学号:" + this.getStudentNo() + "\n性别:"
+ this.getStudentSex() + "\n年龄:" + this.getStudentAge() + "\n所报专业名称:" + sub1.getSubjectName()
+ "\n学制年限:"+sub1.getSubjectLife();
return str;
}
}
学科类
package com.alan.model;
public class Subject {
// 成员属性:学科名称、学科编号、学制年限、选课学生数组、报名选修的学生个数
private String subjectName;
private String subjectNo;
private int subjectLife;
private Student[] myStudents;
private int studentNum;
// 无参构造方法,定义一下,防止出现意外的错误。
public Subject() {
}
// 带参构造方法 对学科名称、学科编号、学制年限赋值
public Subject(String subjectName, String subjectNo, int subjectLife) {
this.setSubjectName(subjectName); // 尽量采用此种赋值方式,而不用this.subjectName=subjectName
this.setSubjectNo(subjectNo); // 因为set方法中有时候会包含一些逻辑校验
this.setSubjectLife(subjectLife);
}
// 带参构造方法 对全部属性赋值
public Subject(String subjectName, String subjectNo, int subjectLife,Student[] myStudent) {
this.setSubjectName(subjectName); // 尽量采用此种赋值方式,而不用this.subjectName=subjectName
this.setSubjectNo(subjectNo); // 因为set方法中有时候会包含一些逻辑校验
this.setSubjectLife(subjectLife);
this.setMyStudents(myStudent);
}
/**
* 获取选修专业学生信息,如果保存学生信息的数组没有被初始化,则初始化长度为200
* @return 保存学生信息的数组
*/
public Student[] getMyStudents() {
if(myStudents == null)
myStudents = new Student[200];
return myStudents;
}
public void setMyStudents(Student[] myStudents) {
this.myStudents = myStudents;
}
public String getSubjectName() {
return subjectName;
}
public void setSubjectName(String subjectName) {
this.subjectName = subjectName;
}
public String getSubjectNo() {
return subjectNo;
}
public void setSubjectNo(String subjectNo) {
this.subjectNo = subjectNo;
}
public int getSubjectLife() {
return subjectLife;
}
// 设置学制年限,必须大于0
public void setSubjectLife(int subjectLife) {
if (subjectLife <= 0)
return;
this.subjectLife = subjectLife;
}
public int getStudentNum() {
return studentNum;
}
public void setStudentNum(int studentNum) {
this.studentNum = studentNum;
}
/**
* 专业介绍的方法
* @return 专业介绍的相关信息 专业名称、专业编号、学制年限
*/
public String info() {
String str = "专业信息如下: \n" + "专业名称:" + this.getSubjectName() + "\n专业编号:" + this.getSubjectNo() + "\n学制年限:"
+ this.getSubjectLife();
return str;
}
public void addStudent(Student stu) {
//1、将学生保存到数组中
for(int i=0;i<this.getMyStudents().length;i++) {
if(this.getMyStudents()[i]==null) {
this.getMyStudents()[i] = stu;
//2、将学生的个数保存到studentNum中
this.studentNum = i+1;
return;
}
}
}
}
测试
package com.alan.test;
import com.alan.model.Student;
import com.alan.model.Subject;
public class NewTest {
public static void main(String[] args) {
Subject sub1 = new Subject("计算机科学与应用","J0001",4);
System.out.println(sub1.info());
System.out.println("=======================");
Student stu1 = new Student("S01","张三","男",58,sub1);
System.out.println(stu1.introduction());
System.out.println("=======================");
Student stu2 = new Student("S02","李四","男",19,sub1);
System.out.println(stu2.introduction("计算机科学与技术", 4));
System.out.println("=======================");
Student stu3 = new Student("S03","王五","女",17,sub1);
System.out.println(stu3.introduction(sub1));
System.out.println("=======================");
sub1.addStudent(stu1);
sub1.addStudent(stu2);
sub1.addStudent(stu3);
System.out.println("学生的总数为:"+sub1.getStudentNum());
}
}
四、Java继承
- 子类继承父类可以直接获取父类的非私有成员(包括属性和方法)
command+shift+o 快速导入包 - 父类不能访问子类的特有成员(包括属性和方法)
方法重写
1、子类重写父类的方法
2、方法名相同、参数列表相同(参数顺序、个数、类型),方法返回值相同
3、方法的访问修饰符是允许有变化的,但是访问范围要大于等于父类的访问范围)
4、与方法参数名无关(string name)可以换成(string month)
访问修饰符
- private 修饰的成员(属性和方法)只能在本类中访问
- public 修饰的成员(属性和方法)访问范围最大,可以在本类、子类、跨包中访问。
- protected 修饰的成员(属性和方法) 可以在本类,同包子类,同包非子类,跨包子类使用。跨包非子类中不允许
-
默认 修饰的成员(属性和方法) 可以在本类,同包子类,同包非子类,不允许在跨包子类/非子类访问
image.png
super关键字
- 父类对象的引用,当子类中重写父类方法,需要调用父类的原有方法,需要加上super关键字。
- 子类构造默认调用父类的无参构造,可以通过super关键字调用父类允许被访问的其他构造方法。必须放在第一行
继承的初始化顺序
-
首先加载父类的静态成员,父类的静态代码块
-
接着加载子类的静态成员,子类的静态代码块
-
父类的成员属性赋值
-
父类的构造代码块
-
父类的构造函数
-
子类的构造代码块
-
子类的构造函数
image.png -
this 与 super 分别指定当前类和父类 不能在静态方法中使用
image.png
Object类
-
Object类是所有类的父类
Object中的 equals 相当于== 比较两个字符串的引用是否相同
String中的 equals 被重写 比较两个字符串的值是否相等 -
tostring也是重写率非常高的一个方法
/*tostring测试 * 1、输出对象名时,默认会调用toString方法 * 2、输出对象为:类型信息+@+地址信息 如:com.alan.animal.Animal@7b1d7fff * */
final关键字
- final修饰class表示该类没有子类,不允许被继承。
- String类 System类都是final修饰的
- final 修饰方法,该方法不允许被重写,但是可以正常被子类继承使用
- final 修饰方法内局部变量,该局部变量不允许被再次修改。
- final 修饰类成员属性,没有赋值的话,只能在构造函数或者构造代码块中进行赋值
- final修饰引用类型的变量,初始化后不能再指向另一个对象,但对象的内容可以改变。
注释简介
快捷键 option+/
@Override 给编译器看的,编译之后注解就没有用了。
五、Java设计模式
image.png单例模式
-
目的:使类的一个对象成为该类系统的唯一的实例。
-
定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供
image.png -
饿汉式代码实现
线程安全的
package com.alan.singleton;
//饿汉式:创建对象实例的时候直接初始化
public class SingletonOne {
//1、创建类中的私有构造
private SingletonOne() {
}
//2、创建该类型的私有静态实例
private static SingletonOne instance = new SingletonOne();
//3、创建公有静态方法返回静态实例对象
public static SingletonOne getInstance() {
return instance;
}
}
测试
import com.alan.singleton.SingletonOne;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
// SingletonOne one = new SingletonOne(); 构造方法已经私有化,这种方式不允许
SingletonOne one = SingletonOne.getInstance();
SingletonOne two = SingletonOne.getInstance();
System.out.println(one);
System.out.println(two); //所有的引用都会指向同一个对象
}
}
- 懒汉式代码实现
线程不安全的
通过1、同步锁 2、双重校验锁 3、静态内部类 4、枚举 解决线程不安全问题。
package com.alan.singleton;
// 懒汉式单例模式
public class SingletonTwo {
// 1、创建私有构造方法
private SingletonTwo() {
}
// 2、创建私有静态实例,静态的实例被共享,一定需要此修饰符
private static SingletonTwo instance = null;
// 3、创建静态公有方法返回静态实例对象
public static SingletonTwo getInstance() {
if(instance == null)
instance = new SingletonTwo();
return instance;
}
}
image.png
六、多态
封装和继承都是为多态准备的,是最核心也是最重要的概念。
多态的必要条件:
1、继承
2、重写
3、父类引用指向子类对象
向上转型
package com.alan.test;
import com.alan.animal.Animal;
import com.alan.animal.Cat;
import com.alan.animal.Dog;
public class Test {
public static void main(String[] args) {
Animal one = new Animal();
/* 向上转型、隐式转型、自动转型
父类引用指向子类的对象,可以调用子类重写父类的方法,以及父类派生的方法,无法调用子类独有方法
小类转型为大类
*/
Animal two = new Cat();
Animal three = new Dog();
one.eat();
two.eat();
three.eat();
}
}
向下转型
package com.alan.test;
import com.alan.animal.Animal;
import com.alan.animal.Cat;
import com.alan.animal.Dog;
public class Test {
public static void main(String[] args) {
Animal one = new Animal();
/* 向上转型、隐式转型、自动转型
父类引用指向子类的对象,可以调用子类重写父类的方法,以及父类派生的方法,无法调用子类独有方法
小类转型为大类
*/
Animal two = new Cat();
Animal three = new Dog();
one.eat();
two.eat();
three.eat();
System.out.println("============================");
/*向下转型、强制类型转换
* 子类引用指向父类对象、此处必须进行强转,可以调用子类特有的方法
* 必须满足转换条件才能转换
* */
Cat temp = (Cat)two;
temp.eat();
temp.run();
}
}
抽象类
-
抽象类不允许实例化,可以通过向上转型,指向子类实例
image.png
抽象方法
不允许包含方法体;子类中需要重写父类的抽象方法。否则,子类也要设置为抽象类。
- 包含抽象方法的类一定是抽象类。
- 抽象类可以没有抽象方法。
接口
package com.alan.tel;
/**
* 具有上网功能的接口
* @author alan
*
*/
//接口可以设置的访问修饰符:public 默认
public interface INet {
//接口中抽象方法可以不写abstract关键字,访问修饰符默认为public
public void network();
//default:默认方法 可以带方法体 jdk1.8之后新增的方法
//可以在实现类中重写,并可以通过接口的引用调用
default public void connection() {
System.out.println("我是接口中的默认方法");
}
//static:静态方法 可以带方法体 jdk1.8之后新增的方法
//不可以在实现类中重写,可以通过接口调用
static void stop() {
System.out.println("我是接口中的静态方法");
}
//接口中可以包含常量,默认会自动加上public final static
int TEMP =20;
}
- 一个类可以实现多个接口
- 继承的父类和实现的接口中有同名方法,默认是实现父类中的同名方法。
- 继承的父类的属性和接口中的常量同名,编译器无法分辨,会报错。
接口也可以实现继承关系,并且可以继承多个父接口
内部类
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
与之对应,包含内部类的类叫外部类。
分为:1、成员内部类 2、静态内部类 3、方法内部类 4、匿名内部类
- 成员内部类
package com.alan.people;
//外部类
public class Person {
int age;
public Heart getHeart() {
return new Heart();
}
//成员内部类
/*
* 1、内部类在外部使用时,无法直接实例化,需要借由外部类信息才能完成实例化
* 2、内部类的访问修饰符,可以任意,但是访问范围会受到影响
* 3、内部类可以直接访问外部类的成员;如果出现同名属性,优先访问内部类中的定义
* 4、可以使用外部类.this.成员方式,访问外部类中的同名信息
* 5、外部类访问内部类信息,需要通过内部类实例,无法直接访问。
* 6、内部类编译后.class文件命名:外部类$内部类.class
* */
class Heart{
public String beat() {
return "心脏在跳动";
}
}
}
package com.alan.people;
public class PersonTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Person lili = new Person();
//获取内部类对象实例,方式1:new 外部类. new 内部类
Person.Heart myheart = new Person().new Heart();
System.out.println(myheart.beat());
//获取内部类对象实例,方式2:外部类对象.new 内部类
myheart = lili.new Heart();
System.out.println(myheart.beat());
//获取内部类对象实例,方式3:外部类调用方法返回内部类
myheart = lili.getHeart();
System.out.println(myheart.beat());
}
}
- 静态内部类
有static修饰,静态内部类对象可以不依赖于外部对象,直接创建。 -
方法内部类
image.png
网友评论