Java面向对象编程(OOP)
面向过程&面向对象
面向过程思想:
- 步骤清晰简单,第一步该做什么,第二步该做什么...
- 适合处理一些较为简单的问题
线性思维
面向对象四思想:
- 物以类聚,分类的思维模式,思考问题首要解决问题需要哪些分类,然后对这些分类进行单独思考,最后才对某个分类下的细节进行面向过程的探索
- 面向对象适合处理复杂的问题,适合处理多人协作问题
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统,但是具体到细微的步骤仍需面向过程的思路去解决
什么是面向对象
面向对象编程:Object-Oriented Programming,OOP
面向对象编程的本质:以类的方式组织代码,以对象的组织(封装数据)
抽象
三大特性:
-
封装
-
继承
-
多态
从认识论的角度考虑是先有对象后有类,对象,是具体的事物。类,是抽象的,是对对象的抽象
从代码运行的角度考虑,是先有类后有对象,类是对象的模板
回顾方法及加深
方法的定义
- 修饰符
- 返回值类型
- break:跳出switch,结束循环 和return的区别
- 方法名(注意规范,驼峰命名 得见名知意)
- 参数列表 (参数名,参数类型)(可变参数:参数类型+...)
- 异常抛出
方法的调用:递归
- 静态方法
- 非静态的方法
- 形参和实参
- 值传递和引用传递
- this关键字
方法回顾
1.什么是方法
package OOP;
//Demo01就是一个类
public class Demo01 {
//main方法
public static void main(String[] args) {
}
/*
修饰符 返回值类型 方法名(参数){
方法体
return 返回值;
}
*/
//return 结束方法,返回一个结果
public static String print(){
return "hello";
}
public void a(){
return;
}
public int max(int a,int b){
return a > b ? a : b; //三元运算符
}
}
2.方法的调用
首先创建一个学生类
package OOP;
//学生类
public class Student {
//静态方法
public static void say(){
System.out.println("学生说话了");
}
public void Say(){
System.out.println("学生没有说话");
}
}
然后去调用学生类
public class Demo02 {
public static void main(String[] args) {
//静态方法调用
Student.say();
//非静态方法调用
//实例化这个类 new
//对象类型 对象名 = 对象值;
Student student = new Student();
student.Say();
}
//同一个类中,两个方法可以互相调用,但是当一个为静态方法,有static,一个为非静态方法,则不行
//若加了static,它和类一起加载的
public void a(){
b();
}
//类实例化之后才存在
public void b(){
a();
}
}
3.实参和形参
public class Demo03 {
public static void main(String[] args) {
//实际参数和形式参数的类型要一一对应
//当下面方法为非静态时这样输出
Demo03 a = new Demo03();
int b = a.add(1,2);
System.out.println(b);
//当方法为静态时这样调用
// int a = Demo03.add(1,2);
// System.out.println(a);
}
//注意该位置有无static
public static int add(int a, int b){
return a+b;
}
}
值传递
package OOP;
//值传递
public class Demo04 {
public static void main(String[] args) {
int a = 1;
System.out.println(a); //输出1
Demo04.change(a);
System.out.println(a); //输出1,输出的依旧是上面那个a,change方法返回值为空
}
//返回值为空,将a=1传给该方法,但是走完之后未将该方法的值再传递回去
public static void change(int a){
a = 10;
}
}
引用传递
package OOP;
//引用传递:对象,本质还是值传递
//对象,内存
public class Demo05 {
public static void main(String[] args) {
Person i = new Person();
System.out.println(i.name); //未对其定义,输出null
Demo05.change(i);
System.out.println(i.name); //未对其定义,输出蔡徐坤
}
public static void change(Person i){
//i是一个对象:指向的是--->Person i = new Person();这是一个具体的对象,就可以改变它的属性
i.name = "蔡徐坤";
}
}
//定义了一个person类,有一个属性:name
class Person{
String name;
}
类与对象的关系
类是一种抽象的数据类型,它是对某一事物整体描述/定义,但是并不能代表某一具体事物
- 动物,手机,电脑
- Person类,Pet类,Car类等,这些都是用来描述/定义某一事物应该具备的特点和行为
对象是抽象概念的具体实例
- 张三这个人就是人的具体实例,他家的狗就是狗的具体实例
- 能够体现出特点,展现出功能的是具体实例,而不是一个抽象的概念
创建与初始化对象
使用new关键字创建对象
在使用new关键字创建的时候,除了分配内存空间外,还会给创造好的对象进行默认初始化及对类中构造器的调用
类中的构造器也称构造方法,是在创建对象时必须要调用的,构造器有以下俩特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
构造器必须掌握
先创建一个学生类
package OOP.demo02;
//学生类
public class Student {
//属性:字段
String name; //null
int age; //0
//方法
public void study(){
System.out.println(this.name+"在学习");
}
}
再创建一个运行方法
package OOP.demo02;
//一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
//类:抽象的,实例化
//类实例化后会返回一个自己的对象
//该地方student对象就是一个Student类的具体实例
Student xiaoming = new Student();
Student dongfeng = new Student();
xiaoming.name = "小明";
xiaoming.age = 18;
System.out.println(xiaoming.name);
System.out.println(xiaoming.age);
dongfeng.name = "东风";
dongfeng.age = 18;
System.out.println(dongfeng.name);
System.out.println(dongfeng.age);
}
}
ps:在类中,应只有属性和方法,启动的main方法应单独在一个类中
作用域
基本使用:
- 1.在java编程中,主要的变量就是属性(成员变量)和局部变量
- 2.我们说的局部变量一般是指在成员方法中去定义的变量
- 3.java中作用域的分类:
- 全局变量:也就是属性,作用域为整体类体,例如:在CAT类,cry,eat等方法使用属性
- 局部变量:也就是除了属性之外的其他变量,作用域为定义在他的代码块中
- 全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值
话不多说,我们直接上代码
package javaSEStudy.basics.OOP.varScope;
public class VarScope1 {
public static void main(String[] args) {
}
}
class Cat{
//全局变量,也就是属性,作用域为整个类体
//属性在定义时可以直接赋值
//在这里sex的作用域就在Cat这个类中
char sex = '雄';
//全局变量可以不赋值,因为有默认值,局部变量必须赋值才能使用,因为没有默认值
//该默认值为0.0
double weight;
public void speak(){
//需要将其赋值,也就是初始化
int num;
//这样直接使用报错,需要给num赋值才能直接使用
// System.out.println(num);
}
public void cry(){
//这里的name,age就是局部变量
//name和age的作用域在cry中,出了这个方法哪儿都用不了
String name = "jack";
int age = 18;
System.out.println("在cry中使用sex属性"+ sex);
}
public void eat(){
System.out.println("在eat中使用sex属性" + sex);
}
}
作用域注意事项和细节使用
- 1.属性和局部变量可以重名,访问时遵循就近原则
- 2.在同一个作用域中,比如在同一个方法成员中,两个局部变量不能重名
- 3.属性生命周期较长,伴随对象的创建而创建,伴随对象的死亡而死亡,局部变量,生命周期较短,伴随着他的代码块的执行而创建,伴随着代码块的结束而死亡,即在同一个方法调用过程中
- 4.作用域范围不同
- 全局变量:可以在本类中使用,或其他类使用(通过方法调用)
- 局部变量:只能在本类中对应的方法使用
- 修饰符不同
- 全局变量/属性可以添加修饰符
- 局部变量不可以添加修饰符
让我们来举个栗子
package javaSEStudy.basics.OOP.varScope;
public class VarScopeDetail {
public static void main(String[] args) {
new Person().speak();
//当执行speak方法时,speak的方法的局部变量比如name就会创建,当speak方法执行完毕后,就将name这个局部变量销毁
//但是属性也就是全局变量仍然可以使用
//第一种跨类
new T().test1();
//第二种跨类
new T().test2(new Person());
}
}
class T{
public void test1(){
//在别的类中调用属性
System.out.println(new Person().name);
}
//把别的作用域的Person传递给它
public void test2(Person p){
System.out.println(p.name);
}
}
class Person {
//属性可以加修饰符,private public protected
String name = "jack";
public void speak(){
//局部变量和全局变量也就是属性可以重名,访问时遵循就近原则
//局部变量不能加修饰符
String name = "king";
System.out.println("speak中的name:" + name);
}
}
- 核心机制:对象引用传递
参数传递本质:Java中方法参数传递均为值传递,但对于对象类型(如Person),传递的是对象的引用地址副本(即指针的拷贝)。
test2(Person p) 的运作:
当调用 test2(new Person()) 时,实际是将新创建的 Person 对象的引用地址复制给形参 p,此时 p 和外部传入的 Person 实例指向堆内存中的同一对象。
Heap: [Person对象(name="jack")] ← 被 p 和外部匿名对象同时引用
2.与 test1 的对比
| 对比维度 | test1() |
test2(Person p) |
|---|---|---|
| 对象创建方式 | 方法内新建 Person 对象 |
接收外部传入的 Person 对象 |
| 内存开销 | 每次调用新建对象,可能增加GC压力 | 复用现有对象,更高效 |
| 使用场景 | 无需外部数据时 | 需要跨方法/类协作时 |
3.潜在问题与注意事项
- 空指针风险:需校验传入参数是否为
null(如if (p != null))。 - 副作用(Side Effect):
若在test2内修改了p.name,外部对象的属性也会同步改变(是否需防御性拷贝取决于设计需求)。 - 线程安全:多线程环境下需考虑引用对象的线程安全性。
构造器详解
package OOP.demo02;
//java----->生成class文件
public class Person {
//一个类即使什么都不写,也会存在一个方法,默认构造器
//显示的定义构造器
String name;
//无参构造
//1.使用new关键字必须要有构造器,本质是在调用构造器
//2.实例化初始值
public Person(){
this.name = "蔡徐坤"; //一旦写了下面的有参构造,这个构造器必须写出来且为空
}
//有参构造
//一旦定义了有参构造,无参就必须显示定义
public Person(String name){
this.name = name;
}
}
/*
public static void main(String[] args) {
//new 实例化一个对象
Person person = new Person("蔡徐坤"); //倘若为无参构造可空,有参得给值
System.out.println(person.name);
}
构造器:
1.和类名相同
2.无返回值
作用:
1.new 本质在调用构造器
2.初始化对象的值
注意点:
1.定义有参构造后,如果想使用无参构造,显示的定义了一个无参构造,也就是空
alt + insert 快捷插入构造器
this. = ;this. 代表当前的值;= 后是传进来的值
*/
简单小结与对象
/*
1.类与对象
类是一个模板,抽象的;对象则是一个具体的实例
2.方法
定义、调用
3.对象的引用
引用类型: 基本类型(8个)
对象是通过引用来操作的:栈 ----> 堆
4.属性:字段filed 成员变量
默认初始化 数字则是0,char则是:u0000 ,boolean:false ,引用:null
修饰符 属性类型 属性名 = 属性值
5.对象的创建和使用
必须使用new关键字去创建对象,构造器 Person person = new Person
对象的属性 caiXuKun.name
对象的方法 caiXuKun.sleep();
6.类:
静态的属性 属性
动态的行为 方法
7.三大特征:
封装
继承
多态
*/
封装
该露的露,该藏得藏
程序设计追求:高内聚,低耦合
高内聚:类内部的数据操作细节自己完成,不允许外部干涉
低耦合:仅暴露少量方法给外部使用
封装:数据的隐藏
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
记住一句话:属性私有,get/set
有时set里面会加一个安全性验证
举个栗子
先创建一个学生类
package OOP.demo04;
//学生类 private:私有
public class Student {
//下面将public换成private就是将属性私有
//名字
private String name;
// public String name;
//学号
private int id;
//年级 grade:成绩,等级,年级
private int grade;
//性别
private char sex;
//年龄
private int age;
//提供一些可以操作这个属性的方法
//提供一些publilc的get/set方法
//get 获得这个数据
public String getName() {
return this.name;
}
//set 给这个属性设置值
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 150 || age <= 0) {
this.age = 0;
}else {
this.age = age;
}
}
//这是方法
//学习()
//睡觉()
/*
alt + insert 也可快捷设置get和set方法
*/
}
然后创建一个测试类
package OOP.demo04;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
// s1.name = "蔡徐坤"; 将private换成pubilc才能这样读取
//被封装后,以下面方式开始读取
String name = s1.getName();
//赋值
s1.setName("牢大");
System.out.println(s1.getName());
s1.setAge(999); //该输入不合法
System.out.println(s1.getAge());
}
}
封装的意义:
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口,形成规范
- 提高系统的可维护性
继承
本质:是对某一批类的抽象,从而实现对现实世界更好的建模
extends的意思是“扩展”,子类是父类的扩展
java中只有单继承,没有多继承
一个儿子只有一个父亲,但是一个父亲可以有多个儿子
- 继承是类与类的一种关系,除此以外,类与类之间的关系还有依赖,组合,聚合
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,用关键字extends表示
- 子类与父类之间从意义上讲,应该具有‘is a’的关系
object类
super
方法重写
我们在企业级应用开发中很少使用继承等,更多的是使用组合
| 方式 | 代码示例 | 适用场景 | 优点 |
|---|---|---|---|
| 构造方法注入 | this.calculator1 = calculator1 | 强依赖 | 明确、线程安全 |
| Setter 注入 | setCalculator1(calculator1) | 可选依赖 | 灵活 |
| 方法参数传递 | handleCalculation(calculator1) | 临时依赖 | 无长期耦合 |
| 接口依赖 | private Calculator calculator | 扩展性强 | 松耦合 |
| 依赖注入框架 | @Autowired private Calculator1 calculator1 | 企业级应用 | 自动化管理 |
选择哪种方式?
- 如果依赖是必须的、不变的 → 构造方法注入(推荐 final 修饰)。
- 如果依赖是可选的、可变的 → Setter 注入。
- 如果依赖只临时使用 → 方法参数传递。
- 如果需要扩展性 → 接口依赖。
- 如果是大型项目 → 依赖注入框架(如 Spring)。
让我们举个栗子
先定义一个父类
package OOP.demo05;
//在java中所有的类都默认直接或间接继承Object类
//Person 人;父类、基类
public class Person {
//public 公共的,子类可以调用
//protected 受保护的,优先级比default高一点
//private 私有的,子类无法调用
//default 默认的
private int money = 10_0000_0000;
//删去private为默认,可以加该关键词,也可以不加,需要其他关键词换掉private即可
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
再给出他的两个子类
package OOP.demo05;
//Student is 人;派生类,子类
//子类继承了父类,就会拥有父类的全部方法
public class Student extends Person {
//crtl + h 指出哪个类继承哪个类,给出关系示例图
}
package OOP.demo05;
//老师 is 人;派生类,子类
public class Teacher extends Person {
}
最后给出测试类
package OOP.demo05;
public class Application {
public static void main(String[] args) {
Student s = new Student();
System.out.println(s.getMoney());
}
}
可以看出,student类中并没有任何属性,但是研究可以调用money这个属性,他继承了person这个类的所有属性,所以可以调用
super详解
直接上代码展示
先写父类
package OOP.demo06;
public class Person {
public Person() {
System.out.println("Person执行无参构造");
}
protected String name = "蔡徐坤";
//假若这里修饰词是priva,即使用super也无法调用
public void print(){
System.out.println("鸡你实在太美");
}
}
再写子类
package OOP.demo06;
public class Student extends Person {
public Student() {
//隐藏代码:调用了父类的无参构造 ----> super();
super(); //调用父类的构造器,必须在子类构造器的第一行
System.out.println("student执行无参构造");
}
//在这里有个问题,假如父类没有无参构造,那么子类也无法进行无参构造
private String name = "基尼太美";
public void test1(String name) {
System.out.println(name); //这是我传给他的参数
System.out.println(this.name); //是类里面的参数
System.out.println(super.name); //用来访问父类里的参数
}
public void print(){
System.out.println("good good study");
}
public void test2(){
print(); //当前类的方法
this.print(); //当前类的方法
super.print(); //父类里面的方法
}
}
再写测试类
package OOP.demo06;
public class Application {
public static void main(String[] args) {
Student student = new Student();
System.out.println("==============");
/*
会输出:
Person执行无参构造
student执行无参构造
这里未对父类进行实例化,但是依旧输出了父类
证明先对父类进行无参构造,再对子类进行无参构造
*/
student.test1("kunkun");
System.out.println("===============");
student.test2 ();
}
}
/*
最终输出结果一览
Person执行无参构造
student执行无参构造
==============
kunkun
基尼太美
蔡徐坤
===============
good good study
good good study
鸡你实在太美
*/
super注意点:
- super调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
VS this
- 代表的对象不同: this ---> 本身调用者这个对象; super ----> 代表父类对象的应用
- 前提:this ---> 没有继承也可以使用;super ----> 只能在继承条件下才能使用
- 构造方法:this(); 默认调用本类的构造 super(); 父类的构造
方法重写
让我们举个栗子
先写一个父类,A类
package OOP.demo07;
//重写都是方法的重写,与属性无关
public class A {
public void test(){
System.out.println("A ----> test");
}
}
再写一个子类,B类
package OOP.demo07;
//继承
public class B extends A {
//override 重写
@Override //注解,有功能的注释
public void test() {
// super.test(); //默认调用父类的方法,也可重写自己的方法
System.out.println("B ----> test");
}
}
最后写一个测试类
ackage OOP.demo07;
public class Application {
public static void main(String[] args) {
//静态方法和非静态方法区别很大
//在静态方法中:方法的调用只和左边定义的数据类型有关
//非静态 -----> 重写
//在重写的方法中,修饰词也只能为public,如果为private,则无法重写
//重写的方法方法名和参数都一样,但是只能子类重写父类的方法
B b = new B();
b.test(); //走b类调用方法
//父类的引用指向了子类
A a = new B();
//在非静态方法中,这叫子类重写了父类的方法
a.test(); //走a类调用方法
}
/*
在A,B类中,方法中加了static静态,输出为
B ----> test
A ----> test
将static删除,输出
B ----> test
B ----> test
*/
}
重写:
- 需要有继承关系,子类重写父类方法
- 1.方法名必须相同
- 2.参数列表必须相同
- 3.修饰符:范围可以扩大,但是不能缩小: public > protected > default > private
- 4.抛出的异常:范围可以被缩小,但是不能扩大
重写:子类的方法必须和父类方法一致,方法体不同
为什么需要重写?
1.父类的功能,子类不一定需要或者不被满足
快捷键:alt + insert ,选中 overide,也就是重写
2.当重写时,idea在代码行数边会出现小图标

多态
可以实现动态编译,增强可扩展性
也就是动态绑定机制:
当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
当调用对象属性时,没有动态绑定机制,哪里声明,就在哪里使用
举个栗子:

B extends A
A a = new B();
//A 是a的编译类型,B是a的运行类型
//我们先把B类的sum方法注销
//当运行到父类也就是A类的时候,A类和B类有一个重名方法getI(),
//它需要去调用这个重名方法,那么它一定是走的B类的方法,所以这里B的return i 也就是20再加10,最后输出30
//再把A类的sum1注销掉,这里返回的是i,由于属性没有动态绑定机制,所以会返回20,父类的i=10 再加上 10,输出20
什么是多态:
- 即同一方法可以根据发送对象的不同而采取多种不同行为方式
- 一个对象的实际类型是确定的,但是可以指向对象的引用的类型有很多(父类或有关系的类)
多态存在的条件
- 有继承方法
- 子类重写父类方法
- 父类引用指向子类对象
注意
多态是方法的多态,属性没有多态性
instanceof
类型转换 --> 引用类型转换
让我们来举个栗子
先创建一个Person类
public class Person {
public void run(){
System.out.println("Student run");
}
}
再创建一个学生类
public class Student extends Person {
@Override
public void run() {
System.out.println("maike run");
}
public void eat(){
System.out.println("eat");
}
}
/*
多态注意事项:
1.多态是方法的多态,属性没有多态
2.父类和子类,有联系,不行会报:类型转换异常 ClassCastException!
3.存在条件: 继承关系,方法需要重写,父类引用指向子类对象 Father f1 = new Son();
有些方法不能被重写:static 方法,属于类,他不属于实例
final 常量
private 该修饰的方法被私有,无法重写
*/
最后的惯例测试类
public class Application {
public static void main(String[] args) {
//一个对象的实际类型是确定的
// new Student();
// new Person();
//可以指向的引用类型不确定:父类的引用指向子类
//Student 能调用的方法都是自己的或者继承父类的!
Student s1 = new Student();
//Person 父类的,可以指向子类,但不能调用子类独有的方法
Person s2 = new Student();
Object s3 = new Student();
//对象能执行哪些方法,主要看对象左边的类型和右边关系不大
s2.run(); //子类重写了父类的方法,执行子类的方法
// s2.eat(); //父类无方法,所以在这里他就不能这样去调用子类中的eat方法
s1.run();
((Student) s2).eat(); //这样写才能从父类调用子类独有的方法
//该写法是强制类型转换,将s2这个高类型转换为了Student这个低类型
}
}
instanceof 和 类型转换
instanceof详解
让我们来举个栗子
首先创建一个person类
public class Person {
public void run(){
System.out.println("run");
}
}
再创建一个student类
public class Student extends Person {
public void go(){
System.out.println("go");
}
}
再创建一个teacher类
public class Teacher extends Person {
}
最后来个测试类
public class Application {
public static void main(String[] args) {
//Object > String
//Object > Person > Teacher
//Object > Person > Student
// x instanceof y 能不能编译通过 接口
Object s1 = new Student();
System.out.println(s1 instanceof Student); //true
System.out.println(s1 instanceof Person); //true
System.out.println(s1 instanceof Object); //true
System.out.println(s1 instanceof Teacher); //false
System.out.println(s1 instanceof String); //false
System.out.println("============");
Person s2 = new Student();
System.out.println(s2 instanceof Student); //true
System.out.println(s2 instanceof Person); //true
System.out.println(s2 instanceof Object); //true
System.out.println(s2 instanceof Teacher); //false
// System.out.println(s2 instanceof String); //编译就报错,person与string没有任何联系
System.out.println("============");
Student s3 = new Student();
System.out.println(s3 instanceof Student); //true
System.out.println(s3 instanceof Person); //true
System.out.println(s3 instanceof Object); //true
// System.out.println(s3 instanceof Teacher); //编译报错,teacher与student无任何关系
// System.out.println(s2 instanceof String); //编译就报错,person与string没有任何联系
}
}
类型转化
让我们举个栗子
除了测试类与instanceof中不一样,其余都一样,在这里就不重复了
public class Application {
public static void main(String[] args) {
//类型之间的转换 基本类型转换 高低(64 32 16 8)
// 父 ------> 子
// 高 ------> 低
//当子类转换为父类,可能会丢失一些自己本来的方法
Person s1 = new Student();
// s1.go; 这样无法直接调用,这是子类独有的方法,父类无法调用
//s1将Person类型,也就是这个对象,转换为Student类型,就可以调用Student类型的方法
((Student) s1).go();
//这两个代码本质相同,下面这个代码是对上面代码最直观的解释
Student s2 = (Student) s1;
s2.go();
}
}
/*
1.父类引用指向子类的对象
2.把子类转换为父类,向上转型:可以直接转换
3.把父类转换为子类,向下转型:需要进行强制类型转换(再基本类型中会丢失精度,在这里可能会丢失一些方法)
4.方便方法的调用,减少重复代码
抽象 : 封装 继承 多态
*/
static关键字详解
static 变量是类中所有对象共享
static 类变量,在类加载的时候就生成
它存在堆中的GC堆中,和Class对象一样
让我们举个栗子
public class Person {
{
System.out.println("匿名代码块"); //2 赋初始值
}
static{
System.out.println("静态代码块"); //1 最先它输出,但是只执行一次
}
public Person() {
System.out.println("构造方法"); //3
}
public static void main(String[] args) {
Person p1 = new Person();
System.out.println("=============");
Person p2 = new Person();
}
/*
静态代码块
匿名代码块
构造方法
*/
}
package OOP.demo10;
//static
public class Student {
private static int age; //静态的变量 多线程
private double score; //非静态变量
public static void go(){
}
public void run(){
go(); //非静态方法也可以这样直接调用静态方法
}
public static void main(String[] args) {
new Student().run(); //通过new一个对象才能用一点去调用这个方法
Student.go(); //go方法在这个类中,他是静态方法,可以直接使用类名去调用这个方法
go(); //也可以猴这样直接调用该方法
// Student s1 = new Student();
//
// System.out.println(Student.age);
//// System.out.println(Student.score); 无法使用类名加一点去调用
//
// System.out.println(s1.age);
// System.out.println(s1.score);
}
{
//代码块(匿名代码块)
}
static {
//静态代码块
}
}
package OOP.demo10;
import java.sql.SQLOutput;
import static java.lang.Math.random; //静态导入包
import static java.lang.Math.PI; //静态导入包
public class Test {
public static void main(String[] args) {
System.out.println(random()); //输出一个随机数,math类中的
System.out.println(Math.random()); //如果没有导包,得这么写
System.out.println(PI);
}
}
理解main方法语法
public static void main(String[] args)
java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
java虚拟机在执行main()方法的时候不必创建对象,所以方法必须是static
该方法接受String类型的数组参数,该数组保存执行java命令时所传递给所运行的类的参数

在运行的时候将参数传入给虚拟机使用也就是 java执行程序 参数1 参数2 参数3
抽象类
abstract修饰符可以用来修饰方法也可以用来修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类
抽象类中可以没有抽象方法,但是有抽象方法的的类一定要声明为抽象类
在抽象类中,不能用new关键字来创建对象,他是用来让子类继承的
抽象方法,只有方法的声明,没有方法的实现,他是用来让子类实现的
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
我们直接来举个栗子
package OOP.demo11;
//abstract 将该类变成抽象类
//extends java里是单继承,没有多继承,类只能单继承,但是接口可以多继承
public abstract class Action {
//约束 有人帮我们实现
//抽象方法,该方法没有实现的功能模块,只有一个方法名
public abstract void doSomething();
}
再创建一个继承他的类
public class A extends Action {
/*因为A继承Action,但是A为抽象类,其中有抽象方法
我们在创建该类去继承的时候就会报错,要求我们必须重写其中被抽象的方法
抽象类的所有方法,继承了它的子类,都必须实现它的方法
*/
//除非它的子类也是abstrict抽象类,那就不用实现
@Override
public void doSomething() {
}
}
/*
特点:
1.不能new这个抽象类,只能通过子类去实现,他就是一个约束
2.抽象类里可以有普通方法,但是抽象方法必须在抽象类中
抽象的抽象:约束
存在意义:一个角色的创建,可以是继承他的方法,然后将公有属性抽象出来,就可以是代码更简洁高效
*/
接口的定义与实现
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口:只有规范,自己无法写方法,专业约束,约束和实现分离:面向接口编程
声明类的关键字是class,声明接口的关键字是interface
接口就是规范,定义的是一组规则,体现了现实生活中“如果你是...则必须能...”的思想,如果你是汽车,则必须能跑
接口的本质就是契约,就像现实中的法律,制定好后大家遵守
OOP的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式,模式都只针对具备了抽象的语言(c++,java,c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象
我们先创建两个接口
package OOP.demo12;
//抽象的思维
//interface 接口的关键字,每个接口都需要有他的实现类
public interface UsersService {
//接口中所有定义的属性都是一个静态常量
//前面使用的关键字是public static final
int age = 64;
//接口中的所有定义方法都是抽象的,其默认为public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
package OOP.demo12;
public interface TimeService {
void timeer();
}
我们再创建一个接口的实现类
package OOP.demo12;
//类可以实现接口,且命名为接口后加Impl,记得在类中写入implement去连接接口
//实现接口的类,必须要重写接口里所有的方法
//从侧面实现了多继承 利用接口
public class UserServiceImpl implements UsersService,TimeService { //该写法实现了多继承,他同时继承了UsersService,TimeService两个接口
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timeer() {
}
}
接口作用:
1.约束
2.定义一些方法,让不同的人实现
3.默认方法前的修饰词都是public abstract,将其抽象了
4.我们定义一个常量,他前面的修饰词为public static final
5.接口不能被实例化,接口中没有构造方法
6.implement可以实现多个接口
7.实现接口必须要重写里面的方法
内部类
内部类就是在一个类的内部再定义一个类,比如,A中定义了一个B类,那么B类对于A类来说就是一个内部类,而A类相对B类来说就是外部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
我们直接来举个栗子
先创建一个外部类
public class Outer {
private int id=5;
public void out(){
System.out.println("这是一个外部类的方法");
}
//class也可以写在方法中,这为局部内部类
public void method(){
class Inner{
}
}
//当在这里加上static是,这个就变成静态内部类,下面就无法获取到id了
public class Inner {
public void in(){
System.out.println("这是一个内部类的方法");
}
//获得外部类的私有属性,私有方法等
public void getId(){
System.out.println(id);
}
}
}
//一个java类中可以有多个class类,但是只能有一个public class
class A{
}
我们再建一个例子类
public class Test {
public static void main(String[] args) {
//匿名内部类
//没有名字初始化类,不用将实例保存到变量中
new Apple().eat();
UserService userService = new UserService() {
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println("eat apple");
}
}
interface UserService{
void hello();
}
浙公网安备 33010602011771号