什么是面向对象 面向过程, 他们是解决问题的思路, 大致的方向
面向过程指的是 分析出问题解决所需要的步骤
弊端 : 费时 费力 做出来的结果也不一定是最优
面向对象
:对于Java来讲, Java是面向对象的语言,对象就是面向对象程序设计的核心。所谓对象就是真实世界的所有实体,对象和尸体是一一对应的,也就是说现实中每个实体都是一个对象,它是一种具体的概念。
对象的特点
1 对象具有属性和行为
2 对象具有变换的状态
3 对象具有唯一性
4 对象都是某个特别的实例
5 一切皆为对象,真实的世界中所有的事物都可以是为对象
例如
在学校里,会有老师和学生等实体,学生有学号 姓名 班级 等属性,也会有学习 提问 走路等操作,学生知识抽象的描述,这个描述成为类,在学校里活动的是学生个体,这些具体的个体成为对象,对象也称为实例。
面相对象的三大特性
:面向对象程序开发模式,更有利于程序员分工合作,提高开发效率。面型对象 有一下优点
1 可重复性:代码重复使用减少代码量,提高开发效率,
2 可扩展性: 执行的功能很容易加到系统中去 ,便于软件的修改
3 可扩展性:能将功能与数据结合,方便管理。
面向对象的核心特性
1
继承性
程序中的继承性是指子类拥有父类的全部特征和行为,这只是类之间的一种关系,Java只支持单继承
封装性
封装是将代码及其处理的数据绑定在一起的一种编程机制,该机制保证了程序和数据都不受外部干扰且不被误用。封装的目的在于保护信息,使用它的主要优点如下。
1 保护类中的信息,它可以阻止在外部定义的代码随意访问内部代码和数据。
2 隐藏细节信息,一些不需要程序员修改和使用的信息,比如取款机中的键盘,用户只需要知道按哪个键实现什么操作就可以,至于它内部是如何运行的,用户不需要知道。
**有助于建立各个系统之间的松耦合关系。**当一个系统的实现方式发生变化时,只要它的接口不变,就不会影响其他系统的使用。例如 U 盘,不管里面的存储方式怎么改变,只要 U 盘上的 USB 接口不变,就不会影响用户的正常操作。
提高软件的复用率,降低成本。每个系统都是一个相对独立的整体,可以在不同的环境中得到使用。例如,一个 U 盘可以在多台电脑上使用。
Java语言的基本封装单位是类,并且Java提供了共有和私有的访问模式,共有代表用户可以知道或应该知道的每件东西,私有的方法数据只能通过类的成员代码来访问,可以确保不会发生不希望发生的事情
、
多态性
指的是一个接口多个方法多态性体现在父类中定义的属性和方法被子类继承后,可以具有不同的属性或表现方式。多态性允许一个接口被多个同类使用,弥补了单继承的不足,如下图
现在用一些代码类表示面向对象
例如我们现在类建立一个名为Point 的对象
class Point{
double x;//定义成员变量x
double y; //定义成员变量y
public double getDistance(Point other){
//定义一个方法getDistance 对象中的方法不需要进行状态的描述没有静动之分
return Math.hypot(x-other.x,y-other.y);
}
}
class Q01{
public static void main(String[] args){
Point.p1 = new Point();
Point.p2 = new Point();
p1.x = 10;
p1.y = 20;
p2.x = 13;
p2.y = 40;
System.out.println(p1.getDistance(p2);
System.out.println(p2.getDistance(p2));
/*
在这里定义一个对象的方法 对象名.变量名=new 对象名();
而这里的对象Point其实就相当于我们定义了一个新的变量类型
就行第一句话可以理解为 新建一个point类型的变量p1
而二到六行是对每个成员变量进行初始化
最后两行是对对象中方法的调用
getDistance(Point other)
其中时第一个是p1进行方法的调用所以是**p1.**distance(p2),
*/
/*
triangle 类
数据 Point p1 p2 p3;
行为 getArea()返回当前三角形的面积
getLength()返回三角形的周长
*/
class triangle{
Point p1;
p1.x=0;
p1.y=10;
Point p2;
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200301150539827.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1F3ZTIwMDAwMA==,size_16,color_FFFFFF,t_70)p2.x=0;
p2.y=0;
point p3;
p3.x=10;
p3.y=0;
public double getArea(){
double side1 = p1.getDance(p2);
double side2 = p2.getDistance(p3);
double side3 = p1.Distance(p3);
double s = (side1 + side2 +side3 )/2;
return Math.sqrt(s*(s-side1)*(s-side2)*(s-side3);
}
public double getLength(){
return p1.getLength(p2)+p2.getLength(p3)+p3.getLength(p1);
}
}
}
对象的内存图解I
1.在堆内存中开辟一个空间并分配地址
2.按照类的描述,在该空间中定义成员变量 并且有默认初始化值
3.加载成员函数进入方法区(只加载一次)
4.对象创建完毕 将空间地址赋值给相应的变量
5.变量(p1/p2)调用成员变量
先通过该变量所存储的地址去堆空间中找
然后在该空间中找相应的成员变量
6.变量(p1/p2)调用成员函数
直接去方法区中找该成员函数
将该函数加载进栈内存开始运行
为了方便区分哪个对象调用的该成员函数
由this这个关键字段 来区分 this主要存的是当前对象的地址
注意:当成员函数在操作变量的时候
先在当前函数的空间里找 局部变量
如果没有找到,再去this所指向的堆内存中对象所属空间里去找
封装和private关键字
private 关键字就是一个权限关键字 public protected 默认不写
private关键子表示四有权限 该成员变量或者成员函数只能在类中被访问,外界并不刻意可以, 目前有两个问题
1 我们可以直接通过对象直接修改成员变量
弊端:如果赋予一个错误的值 那么势必会导致程序后期的运行结果错误 如何解决呢?
class Person{
int age;
}
Person p =new Person();
System.out.println(p.age);
p.age = 10;
/*这样的话 只是访问并打印 不符合实际的业务逻辑
*/
所以为了防范外界直接对对象的成员变量进行修改 在这里我们使用private 关键字
但是一旦加了private 关键字的话 就不能对其进行修改和获取了
此时就死局了~因为不能保证队成员变量进行后期的获取和修改 那么本质上来讲 我们应该防范的是什么呢 无条件对成员变量进行强制修改就是说 修改是个已修改的 但是要考虑输入值的正确性(加上 if-else)
setter 是一个Java中的一个规范 修改器 主要负责修改成员变量 其本身就是一个函数 一般命名为 set+名称 setName
getter 是一个访问器 主要负责访问成员变量 (返回成员变量) getter看需求设立
建议 今后的代码中其中的成员变量一律私有 然后再去设置访问器和修改器
注意 如果成员变量和局部变量重名了 如何区分呢 只需要在成员变量前加上一个this即可
(每一个的成员变量前面默认有this.)
成员变量局部变量的区别
1 储存位置
局部变量储存在栈内存中函数所处的空间内
成员变量 储存在堆内存中对象的所属空间内
2 生命周期
局部变量 随之函数进栈而存在 随函数弹栈而消失
成员变量 随着对象的创建而存在,随着对象的销毁而消失
3.访问范围
局部变量的访问范围仅仅在函数当中
成员变量的访问范围在当前类中
4.初始化值
局部变量必须先进行初始化 之后再能被调用
成员变量在创建时有默认初始化
构造函数
我们只能将对象创建完毕之后,再进行对成员变量的赋值
对于一个人而言 是出生后才有的性别 还是出生前就有性别
有些人 出生前就有姓名了 有些人出生后才有的姓名
隐喻的对象
有些对象创建之前成员变量就有值(不含默认初始化)
有些对象创建之后成员变量才有值
所以就有一个问题了 如何在创建对象之前之中对我们的成员变量进行赋值呢?
构造函数的主要作用:在创建对象之时对成员变量进行赋值操作
构造函数的格式:
权限修饰符 函数名(参数列表){
函数体;
}
对比和之前学过的函数来说
构造函数
没有函数类型关键字
没有返回值类型(并不意味着没有return)
函数名必须是类名
但凡创建一个对象 构造函数就执行一次
问题:我们之前并没有写构造函数
如果类中没有定义任何构造函数的情况下,则会有一个默认无参的构造函数
public ClassName(){}
如果类中有明显定义的构造函数 则默认无参的构造函数不存在了
所以 一般而言 我们最好将那个无参的构造函数写出来!!!!!!!
成员变量的赋值其实经历了三个阶段
默认初始化- 显式初始化 - 针对性初始化
类中成员变量被赋值 构造函数
构造函数内存图解
1.在堆内存中开辟一个空间并分配地址
2.对成员变量进行【默认初始化】
3.相应的构造函数进栈 刚开始就对成员变量进行【显式初始化】
4.接着再去执行构造函数中的内容【针对性初始化】
5.构造函数执行完毕 弹栈 将对象的地址赋值给相应变量即可
/**
仿照Java自带的String类 写一个自己的MyString类,
基本支持String中大部分的功能。
其实String的本质就是一个一维的字符数组,然后再加上其相关的操作
数组的长度一旦确定 则不可更改;
String的长度一旦确定 也不可更改;
更重要的是,String的内容一旦确定 也不可更改;
String是一个不可变对象 意味着我们不能在原地修改String的内容 只能重新创建一份进行修改
*/
class MyString{
private char[] value;
/**
创建一个空串"",也就是说字符串长度为0
*/
public MyString(){
this.value=new char[0];
}
/**
创建一个由指定字符编码组成的字符串
*/
public MyString(byte[] bytes){
this.value=new char[bytes.length];
for(int i=0;i<value.length;i++){
value[i]=(char)bytes[i];
}
}
/**
创建一个由指定字符组成的字符串
*/
public MyString(char[] value){
this.value=new char[value.length];
for(int i=0;i<value.length;i++){
this.value[i]=value[i];
}
}
public char charAt(int index){
if(index<0||index>=value.length){
System.out.println(">>>角标越界"+index);
return (char)-1;
}
return value[index];
}
public int indexOf(int ch){
for(int i=0;i<value.length;i++){
if(value[i]==ch){
return i;
}
}
return -1;
}
public int length(){
return value.length;
} //比较两个字符串之间的大小
//两个字符数组从左到右挨个比较元素 如果出现第一个不相等的元素 返回差值即可
//如果某一个数组遍历完了也没有发现不相等的 返回长度的差值即可
public int compareTo(MyString other){
int min=Math.min(length(),other.length());
for(int i=0;i<min;i++){
if(value[i]!=other.value[i]){
return value[i]-other.value[i];
}
}
return length()-other.length();
}
public boolean contains(MyString other){
char[] c1=value;
char[] c2=other.value;
if(c2.length>c1.length){
return false;
}
for(int i=0;i<c1.length;i++){
boolean flag=true;
for(int j=0;j<c2.length;j++){
if(c2[j]!=c1[i+j]){
flag=false;
break;
}
}
if(flag){
return true;
}
}
return false;
}
}
//需求:实现一个简单的栈结构Stack
class Test01{
public static void main(String[] args){
Stack stack=new Stack(20);
for(int e=1;e<=20;e++){
stack.push(e);
}
stack.push(21);
System.out.println(stack.size());
System.out.println(stack.toString());
System.out.println(stack.peek());
System.out.println(stack.pop());
System.out.println(stack.toString());
System.out.println(stack.isEmpty());
stack.clear();
System.out.println(stack.toString());
System.out.println(stack.pop());
}
}
Stack是一个简单的由一维数组实现的栈结构
支持入栈出栈等常见操作,但不支持动态扩容操作
为了方便简化代码,默认此Stack中只能存储int型数据
public class Stack{
private int[] data; // 容器 用于存储栈元素 data.length表示栈的最大容量
private int top=-1; // 栈顶标记 用于标记栈顶元素的位置 当栈为空时 top=-1 栈中有效元素的个数top+1
private int capacity=10; //默认最大容量为10
/**
创建一个默认容量为10的栈
*/
public Stack(){
this(capacity);
}
/**
创建一个指定容量为capacity的栈
@param capacity 由调用者传入的指定容量
如果capacity<0 则容量置为0
如果capacity>100 则容量置为100
*/
public Stack(int capacity){
if(capacity<0){
capacity=0;
}
if(capacity>100){
capacity=100;
}
this.data=new int[capacity];
}
/**
将元素e入栈,如果栈当前已满,则无法加入
@param e 用户指定入栈的元素
*/
public void push(int e){
if(size()==data.length){
System.out.println(">>>栈已满,无法添加元素"+e);
return;
}
data[++top]=e;
}
/**
从栈中弹出一个元素,如果栈已经是空,则返回-1即可(没有学异常 -1表示错误)
@return 返回当前栈顶的元素,如果栈为空则返回-1
*/
public int pop(){
if(isEmpty()){
System.out.println(">>>栈为空,无法弹栈元素");
return -1;
}
return data[top--];
}
/**
获取当前栈顶元素(不出栈),如果栈已经是空,则返回-1即可
@return 返回当前栈顶的元素,如果栈为空则返回-1
*/
public int peek(){
if(isEmpty()){
System.out.println(">>>栈为空,无法获取栈顶元素");
return -1;
}
return data[top];
}
/**
判断当前栈是否为空
@return true表示栈空 否则栈不为空
*/
public boolean isEmpty(){
return top==-1;
}
/**
清空当前的栈
*/
public void clear(){
top=-1;
}
/**
获取栈中有效元素的个数
*/
public int size(){
return top+1;
}
/**
返回栈的字符串表现形式
*/
public String toString(){
if(isEmpty()){
return "[]";
}
String s="[";
for(int i=0;i<size();i++){
s+=data[i];
if(i==size()-1){
s+="]";
}else{
s+=",";
}
}
return s;
}
}
this.关键字
当前的类的一个引用-------当前的对象
this.构造方法 当前引用调用类中相应的方法
this.成员变量 对类的成员进行初始化
this 可以当返回值使用 ?返回结果是相当于本类的对象
//Person 希望求出两个person对象的年龄差
重载和重写的区别
函数重载(Overload)
:1 发生在同一个类中
java 继承
class Student{
String name '
int age;
void eat'
void study;
}
class Worker{
String name;
int age;
void eat;
void work(){
}
}
class Person{
String name;
int age;
void eat;
}
当我们在定义若干类的时候,发下一些具有相同属性和行为 ,那么我们将这些相同部分进行抽取,独立商城一个类,我们称这个新生成的类为之前有共同属性的类的父类。
他们之间的关系是继承关系,用 extend 来表示
在这里Person 是worker 和student 的父类
在Java中继承是单继承(一个子类只有一个父类,而一个父类可以有好多子类)
在c++中,集成式多继承的(但是不代表Java就一定是单继承的,Java在接口处是单继承的)
(我们常用的继承体系: 异常体系 集合体系 IO体系 网络体系 多线程体系)
在Java的继承体系中 ,所有 继承体系中所有最终的父类都是Object如果我们在定义一个类的时候,如果没有定义一个父类的话,那么父类默认为 Object
注意
class bird{
String name;
int age;
void fly(){
};
void eat(){
};
}
class bird extends Person{
void
}
这样写是可以的,但是Java是模拟现实情况的语言,不建议使用反伦理的关系,符合社会常识问题`
class ExtendDemo{
public static void main(String[] args){
Zi zi = new Zi();
}
}
class Zi extend Fu
}
class Fu{
static int num = 30;
}
如果只有子类有且非私有,就调用子类的
吐过只有父类有且非私有,就调用父类的
如果父类和子类的都有且非私有,那么就调用子类的
(长远变量之间,是不存在重写关系来的)
子类.属性 顺序:子类成员对象->子类静态->父类成员->父类静态
子类成员函数在内部调用变量时 局部变量->子类对象成员->子类
字符类在函数的特点
class ExtendsDemo02{
public static void main(String[] args){
Zi zi=new Zi();
System.out.println(zi.num1);//20
System.out.println(zi.num2);//30
System.out.println(zi.num3);//50
zi.show();
zi.sing();
}
}
//子父类中成员变量的特点
/*
class Grandfather{
int num1=100;
int num4=200;
}
*/
class Fu {
//extends Grandfather{
int num1=10;
static int num2=40;
static int num3=50;
Fu(){
super();//Object(){} 显示初始化父类空间中的成员变量
System.out.println("Fu constructor1......");
}
Fu(int num){
System.out.println("Fu constructor2......");
}
void sing(){
System.out.println("Fu sing......");
System.out.println("爸爸我会唱红歌");
}
}
class Zi extends Fu{
int num1=20;
static int num2=30;
Zi(){
this(0,0);
System.out.println("Zi constructor1......");
}
Zi(int num1){
this(0,0);
System.out.println("Zi constructor2......");
}
Zi(int num1,int num2){
super();
System.out.println("Zi constructor3......");
}
void show(){
//局部变量和成员变量如果重名了 this区分
System.out.println(num1+","+num2+","+num3);
//局部变量/成员变量和父类变量重名了 super区别 super.xxx => 你爸爸的xxx
System.out.println(super.num1+","+super.num2+","+num3);
//System.out.println(super.super.num1);
//System.out.println(num4);
}
@Override
void sing(){
System.out.println("Zi sing......");
System.out.println("儿子我会唱摇滚、民谣、爵士、蓝调、电子");
}
}
现像 子类在构造函数在调用运行的时候,先执行弗雷德构造函数
在子类的构造函数中,有一句默认的super(...)隐藏了,而且这句代码必须
对super 的调用必须是构造器中的第一个
子类继承父类必然会继承到父类的一些数据,所以在子类构造函数中,必须线上父类把这些数据进行初始化才能继承给子类
注意 父类的构造函数被调用 但不代表就被创建对象了
static关键字
成员变量+static=静态变量
当我们在设计类的时候 发现多个对象中有共享数据的时候 我们就可以把这个共享的数据 定义为静态的
name age虽然每个对象都有 但是值不一定一样 对象的特有数据->成员变量
country虽然每个对象也都有 但是值一样 对象的共有数据-> 静态变量
静态的东西从堆中对象的空间里抽取出来了 放到哪里了呢?静态方法区
成员函数+static=静态函数
静态函数意味着就是对象的共有行为吗?不行的
我们当前的成员函数如果不访问任何成员变量的情况下 这个函数就可以定义为静态的
这就意味着 静态函数不能直接调用当前类中的成员变量 无法从静态上下文中引用非静态
为啥?静态的生命周期要长于对象
静态是优先于对象存在的
静态也称之为是类的成员,我们可以直接通过类去调用
类.静态成员
灵魂拷问:为什么主函数是静态的?
主函数是程序的入口,优于其它运行
假设主函数是非静态的 那么必须先创建对象 才能调用主函数
怎么去创建对象呢?是不是需要写代码?这段代码咋运行呢?
静态变量有没有默认初始化值?有 (运行时常量 永久代 JVM)
也有显示初始化
**类的分类:**
实体类:
就是为了描述一个事物 Person Point
类中内容基本上都是成员函数/成员变量
也会而存在一个静态成员
工具类
提供一些已经被实现好的功能 向外部供应
所以工具类中基本上全都是静态函数
类.Xxx 类.Xxx() Math Arrays
为啥工具类中都是静态?
1.共有的
2.长时间存在
3.加载一次 后期随便使用
一个道理:
当我们钉一个钉子的时候 找榔头
你是造一个榔头 还是用已存在的榔头?
如果工具类可以被创建对象 是不是就想用的时候去创建
效率较低
主类/测试类
主要用于运行/测试代码
这个类中会有主函数的存在
实体类是可以存在主函数的 但是不推荐
工具类一般不会存在主函数
建议 一个类一个文.java件
查漏补缺:
1.静态函数中,是否存在this这个关键字?不可能
2.静态变量,我们在定义的时候,一般而言这个变量是不可修改的 加上final修饰
静态变量与成员变量的区别
1.生命周期
成员变量随着对象的创建而创建 随着对象的消亡而消亡
静态变量随着类的加载而创建 随着程序结束而消失
2.调用方式
成员变量必须先创建对象 在通过对象去调用
静态变量可以被对象调用 也可以直接用类调用
3.存储位置
成员变量存在于堆内存中对象的所属空间里
静态变量存在于静态方法区中类的所属空间里
4.命名
成员变量-对象的特有属性
静态变量-对象的共有属性 类成员
代码块
代码块 {
... }
局部代码块:存在于函数当中(包括函数) for(){
...} if(){
...}
构造代码块:直接在类中出现的{
...}
当对象创建一次 构造代码块执行一次
作用等同于构造函数
静态代码块:直接在类中出现的static{
...}
当类被加载的时候 仅且只执行一次
作用于 对类进行一些初始化操作 JDBC
public class CodeBlock {
{
System.out.println("gouzao");//构造代码块 直接在类中出现{...}
//当对象创建一次就执行一次 最用等同于构造函数
}
static {
System.out.println("jingtai");//静态代码块
// 当类被加载的时候 ,仅且执行一次 作用域 对类进行初始化操作
}
public static void main(String[] args) {
CodeBlock c1 = new CodeBlock();
CodeBlock c2 = new CodeBlock();
CodeBlock c3 = new CodeBlock();
}
}
6.12 单例模式
设计模式:就是我们的前辈们总结出来的一些编码技巧
它并不是随着Java的诞生而诞生的
它是由Java的广大使用者总结出来的一套编码经验
常见26种
单例模式:使用场景是 某一个类只能创建一个对象
比如某一个朝代的皇帝 只能是唯一的
1.既然只能创建一个对象的话 就得不能让外界去创建对象
限制使用new不现实
只能从对象的创建流程中考虑 只要有一个步骤不行 对象就创建不出来
开辟空间分配地址 是由计算机底层决定 我们也控制不了
构造函数执行 只需要将构造函数私有化即可
2.既然外界不能创建对象 我们还得保证对象的创建
所以我们只能在类内部创建对象
Single s=new Single();
能否写成 成员变量的形式?
所以private static
3.内部创建出对象 还得向外界提供
因为private 外界不能直接访问
所以见解 向外界提供一个函数 外界通过调用函数获取对象
多态
多种状态:就是指一个对象可以有多种状态(他在继承体系中的位置变化)
在Java当中 多态代码表现 就是 父类引用指向子类的对象
多态的前提:继承 重写
多态的好处:对代码的功能进行了可扩展性
子类对象可以当做父类对象去使用 但是有限制 只能调用父类函数或重写的函数
不能使用子类特有的行为
多态当中 成员变量的特点:只能访问父类中的成员变量
多态当中 成员函数的特点:
如果子类没有重写 且父类中有 调用父类的
如果子类有重写 调用子类重写的
如果不存在重写 父类没有 那就报错了!