Java面向对象
1.对象
(1)造车:
1.画图纸
定义车的属性:color,speed,seat
定义车的动作:车能跑
2.拿着图纸找工厂造车
面向对象的世界里:
1.类就是图纸:
属性:这一类事物拥有的共同属性
动作:这一类事物共同执行的功能
2.对象:使用类创建的具体的某一个东西
对象能干什么完全取决于类是如何定义的 !
代码:
1.类:要使用class来定义
2.属性:要用成员变量来描述, 直接写在类中的变量
3.动作:成员方法,不写static就是成员方法
4.对象:类 引用 = new 类();
public class Car {
//成员变量
String color; //颜色
int speed; //速度
int seat = 5; //每辆车的座位都是5
//成员方法
public void run(){
System.out.println("车能跑");
}
//main函数是程序的入口,可以出现在任何Java类中
public static void main(String[] args) {
int a = 10; //写在方法里的变量,局部变量
//创建对象,Car是一个引用类型,它和int一样也是类型的一种
//Java分两种数据类型:1.基本数据类型 2.引用数据类型 String和我们创建的所有类
Car c = new Car(); //创建对象, 创建了一辆车,想要使用这俩车就得用c来访问
//让车去跑,对象或者引用.方法() ,.表示调用,可以把它翻译成"的"
c.run();
c.color = "绿色";
c.speed = 120;
//c.pailiang = 1.5; //图纸中没有定义的内容不可以使用
//第二辆车
Car c2 = new Car();
c2.color = "红色";
c2.speed = 180;
System.out.println(c.color+","+c.speed+","+c.seat);
System.out.println(c2.color+","+c2.speed+","+c2.seat);
}
}
总结:
面向对象中:你想让事物Car有什么属性,就来写变量。你想让事物Car去做什么事,就来写方法,造了对象之后,想让它干嘛,就去调用它的方法。
(2)this关键字:
this表示当前类的对象,就是当前正在执行的某个方法。
this可以在方法内部获取到对象中的属性信息。
this还可以区分局部变量和成员变量。(加了this.就是成员变量,若是直接写个变量他会先去找局部)
public class Car2 {
String color;
int speed;
int seat = 5;
public void run(){
//想要获取到车的颜色和速度
System.out.println("车能跑");
}
public void fly(String color){
System.out.println(this.color+"颜色的车会飞,飞在"+color+"颜色的云彩里");
//变量查找顺序,先找自己方法内,如果自己没有,就去this里面找
}
public static void main(String[] args){
Car2 c = new Car2(); //车中的属性就是类中定义好的成员变量
c.color = "红色";
c.speed = 120;
c.run(); //在调用方法的时候,Java会自动把对象传递给方法,在方法中由this来接收对象
Car2 c2= new Car2();
c2.color = "绿色";
c2.speed = 180;
c2.run();
//this可以帮我们区分成员变量和局部变量
Car2 c3 = new Car2();
c3.color = "黄色";
c3.fly("黑色");
// System.out.println(this.color+"颜色的车会飞,飞在"+color+"颜色的云彩里");
// 前面这个加了this他会去找对象里面的颜色,后面color没加this默认先找他自己的方法的颜色。
}
}
2.构造
(1)大侠:
在创建对象的时候,自动调用的方法。
语法:
public 类名(传参){ }
注意:
- 没有返回值这一项。
- 在我们new的时候,自动调用构造方法。
作用:在创建对象的时候,给对象设置属性信息
Java会默认自动的送给每一个类一个无参数的构造方法,但是,如果你写了构造方法,系统就不再赠送了。
public class Car3 {
String color;
int speed;
int seat = 5;
//Java会自动赠送给每一个类一个无参数的构造方法
//如果你自己定义了这个构造方法,那么Java就不再赠送了
//在创建对象的时候,自动调用方法
public Car3(String color, int speed){
this.color = color;
this.speed = speed; //需要用this进行初始化
}
public void run(){
System.out.println(this.color+"颜色的车在跑。");
}
public static void main(String[] args) {
Car3 c1 = new Car3("绿色",120);
// c1.color = "绿色";
// c1.speed = 120; 因为有了构造方法,可以直接在创建对象时传参,因此不需要在下面手动传参了
Car3 c2 = new Car3("红色",180);
// c2.color = "红色";
// c2.speed = 180;
c1.run();
c2.run();
}
}
(2)重载:
构造方法也是方法,也可以进行重载
作用:可以有更多的方式去创建对象
使用this可以访问当前类中其他的构造方法
public class DaXia {
String name;
String waihao;
int age;
String bangPai;
//当构造武松有外号,而岳不群没有外号时,一个构造函数产生分歧,我们需要两个构造函数
//第一个构造方法来满足武松,第二个构造方法来满足岳不群
public DaXia(String name, int age, String bangPai){
this.name = name;
this.age = age;
this.bangPai = bangPai;
}
//构造方法也是方法,他也可以重载
//可以让我们有更多的方式可以创建对象
public DaXia(String name, int age, String bangPai, String waihao){
this(name, age, bangPai);//this还可以调用当前类中其他的构造方法
this.waihao = waihao;
}
public static void main(String[] args) {
//岳不群
DaXia dx1 = new DaXia("岳不群", 18, "华山派");
//武松
DaXia dx2 = new DaXia("武松", 19, "梁山", "行者")
}
}
3.封装
(1)static静态
static:静态
需要将国籍两人的国籍都改成民国。
有多少人就要改多少次, 所以能不能把国家这个属性公共出来, 当提到改国籍时只改一份就可以了。
我们可以使用static静态,静态的内容在内存中时是只保留一份的,并且各个对象之间进行共享。
public class Person {
String name;
static String country = "大清"; //它是共享的
String address;
public Person(String name, String address){
this.name = name;
this.address = address;
}
public static void main(String[] args) {
Person p1 = new Person("赵铁柱", "八大胡同");
Person p2 = new Person("薛白","朝阳门");
//大清亡了,需要换国籍
//推荐使用类名去访问静态的内容
Person.country = "民国";
}
}
使用static将国籍改为静态:
static String country = "大清"; //它是共享的
修改时使用类名去访问静态的内容:
Person.country = "民国";
static静态特点:
1.数据共享
2.属于类的,并不属于对象
3.静态是优先于对象产生的
通用构造器,静态构造器
public class Test{
{
System.out.println("这里是通用构造器");
}
static{
System.out.println("这里是静态构造器");
}
public static void main(String[] args){
new Test();
}
}
/*
打印出的结果为:
这里是静态构造器
这里是通用构造器
构造方法
*/
由此可得:创建对象的过程(简单):
1.先执行静态构造器
2.然后执行通用构造器
3.最后执行构造方法
(2)包和导包
新建包: 右键new 找到Package
//包的声明,表示当前类,从属于com.cnblogs.`ningyao-wenrou`这个包
package com.cnblogs.`ningyao-wenrou`
public class project {
}
使用import
导入包到当前文件,必须先写package
声明,再写import导入。
也就是说package
必须写在有效代码的第一行。
包的本质上就是文件夹,在代码中需要写package 包名;
在自己的包里则不需要导包。
java.lang
包下的东西都不需要导包:String
、System.out.println()
都属于java.lang
包里.
(3)权限访问
Java里提供了四个访问权限:
1.public
任何类都可以访问。适用于需要被其他任何类使用的类、方法或变量。
2.default
包访问权限,在自己包内可以随意访问。适用于仅希望在同一包内共享的成员。
3.private
私有的,只有定义该成员的类可以访问。适用于需要封装的成员,以防止外部访问。
4.protected
只有同一个包中的类和所有子类可以访问。适用于需要在继承关系中共享的成员。
package com.xyq.entity;
public class Person2 {
public String pub = "public"; //公共的
private String pri = "private"; //私有的
String def = "default"; //包内的,默认的
public static void main(String[] args) {
Person2 p = new Person2;
}
}
(4)getter和setter
成员变量使用private
来声明,保护成员变量不被胡乱的赋值
Setter
:主要是给成员变量赋值,做一定的保护
Getter
:从成员变量中获取数据
私有变量无法访问,可以给它定义在方法里面,让别人可以调用这个方法访问给它赋值
package com.xyq.entity;
public class Person {
//成员变量用private给私有化
private String name;
private int age;
// 这个时候成员变量被private给私有化了,其它人无法访问成员变量
// 这个时候我们可以把私有化给变成一个方法,这就是setter
public void setName(String name){
this.name = name;
}
//还可以在setter中加入逻辑判断
public void setAge(int age){
if (age<0){
this.age = 0;
}else{
this.age = age;
}
}
//getter
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public void chi(){
System.out.println(this.name+"在吃东西");
}
}
调用方法给私有变量放值和拿值
package com.xyq.entity;
public class TestPerson {
public static void main(String[] args) {
Person p = new Person();
//p.name = "薛白";
//p.age = -1; //访问不到私有变量
//现在调用方法就可以访问私有变量了
p.setName("薛白");
p.setAge(18);
System.out.println(p.getName());
System.out.println(p.getAge());
p.chi();
}
}
一般情况下,我们需要用idea自动生成get和set的代码,否则很容易写错:
在空白处右键 => Generate => Getter and Setter =>选中想要生成的get和set的私有变量,就可以靠idea自动生成get和set的代码。
(5)final
final表示最终的,不可以再改变的。
1.被final修饰的变量不可以被改变,又被称为常量
2.被final修饰的方法不可以被重写。
3.被final修饰的类 不可以被继承。也就是说,被final修饰过的类不能当做父类被继承,因为子类继承后可以对父类进行扩展和修改,但被final修饰的类已经是最终版本了,不能继承后进行修改。连继承都不准!
代码 –> 不可以被改变:
public class Diamouds{
final int weight = 10; // 用final修饰的变量
public static void main(String[] args){
Diamouds d = new Diamonds();
d.weight = 5 //!!此处会报错,因为weight已经被final修饰了,10已经是他的最终的值,不可以再改变。
}
}
代码 –> 不可以被重写
public final void bling(){ //用final修饰父类的方法
System.out.println("不灵不灵的")
} //这是在父类中定义的方法
//下面在子类中进行重写
public void bling(){
System.out.println("布鲁布鲁的") //!! 此处会报错,因为在父类中bling这个方法被final修饰了,它在父类中已经是最终的结果,不可以再改变
}
代码 –> 不可以被继承
//父类
public final class Diamonds{ //这个类被final修饰了
......
}
//子类
public class PinkDiamonds extends Diamods{ //!!此处会报错,因为父类被final修饰之后代表了最终的结果,不能被继承
}
总结: 就是final不可变
4.继承
(1)概念:
子类可以自动拥有父类中除了私有内容外的其他所有内容。
当出现x是一种y的时候,x就可以继承y
语法:
public class 类 extends 父类{
}
子类可以继承父类里除私有内容外的所有内容。
作用:简化代码开发
相当于子类对父类进行了扩展
(2)super关键字:
super
:表示父类中的内容
语法:
System.out.println(super.name+"在吃桃子");
this
:表示自己类中的内容
用super
和this
来区分父类和子类中重名的内容
public class SunWuKong extends Hero{ //继承父类Hero
String name = "孙大圣";
public void chi(){
System.out.println(this.name+"在吃桃子"); //这句代码会打印出孙大圣在吃桃子,name会先找自己类,然后找父类
//想要看到父类中的name,则使用super.name
System.out.println(super.name+"在吃桃子"); //打印出父类的name,英雄在吃桃子
}
}
super(传参);
可以还原程序,在子类构造方法的第一行,默认调用父类的构造方法
public class SunWuKong extends Hero{ //继承父类Hero
String name = "孙大圣";
public SunWuKong(){
super(name:"武大郎") //还原程序,在子类构造方法的第一行,默认调用父类的构造方法
System.out.println("我是子类的构造方法");
}
总结:
1.super可以获取到父类中的内容
2.可以调用父类中的构造方法,调用的时候必须写在子类构造方法的第一行,如果父类的构造是无参数的,可以不写参数;如果父类没有无参数的构造,必须要进行传参
(3)方法的重写
重写:子类对父类中提供的方法进行重新定义
语法:子类和父类中的方法声明完全一致
父类:
public class LiYuan{
public void makeCountry(){
System.out.println("李渊想建立一个自己的国家")
}
}
子类重写:
public class LiShiMin extends LiYuan{
//重写
public void makeCountry(){
super.makeCountry(); //利用super调用父类中被重写的方法
System.out.println("李世民想建立一个自己的国家")
}
public static void main(Stirng[] args){
LiShiMing lsm = new LiShiMing();
lsm.makeCountry(); //若没有重写,打印出来的依旧是李渊而不是李世民
}
}
重写又被成为方法的覆盖。
半覆盖:
重写后,若还想调用父类中被重写的方法,可以使用super:super.makeCountry();
5.多态
(1)概念:
同一个对象拥有多种形态。
(2)应用场景:
假设有猫、狗、大象三种动物,三种动物都有吃东西这个方法。
而人要投喂这三个动物,则需要写三个方法,即:喂猫、喂狗、喂大象
但有了多态之后,把猫、狗、大象的形态转化为动物,则人只需要写一个方法即可,即:喂动物。
有了这个想法,此时我们就可以创建一个动物类,然后让猫、狗、大象全部去继承动物类,然后在创建对象时可以用父类的变量去创建子类的对象,即:Animal ani = new Cat();
这叫做向上转型。
向上转型的结果就是把猫、狗、大象当成了动物来看。
(3)作用:
把不同的数据类型进行统一,让程序具有超强的可扩展性。
public class Person{
public void feed(Animal ani){ //传参传过来的是什么动物,就让这个动物去吃
ani.eat(); //Animal ani = new Cat();因为将一个猫对象赋值给ani,所以这一段表面上是动物在吃,但本质上仍旧是猫在吃。
}
}
1.把子类的对象赋值给父类的变量 –> 向上转型
2.把父类的变量转化为子类的变量 -> 向下转型
向下转型有风险,Java要求必须要写强制类型转换,即(转换后的数据类型)变量
缺点: 向上转型,会屏蔽掉子类中特有的方法,因为站在动物的角度上去看一只猫,猫会抓老鼠,但是动物不一定会抓老鼠,所以这个时候需要用到向下转型,将动物转换回来猫。
向下转型
Cat cc = (Cat)ani1;
cc.catchMouse();
此时猫又可以调用抓老鼠的方法,如果没有向下转型,直接用ani.CatchMouse();
就会报错,因为站在动物的角度动物是不能抓老鼠的。
6.抽象
(1)概念:
在现实中:就是不存在的东西,只通过想象出来的。
在Java中:只声明,不实现。
(2)使用:
抽象方法:使用abstract来修饰,不可以有方法体,直接分号结束即可。
抽象类: 如果一个和类中有抽象方法,那么这个类也必须是一个抽象类,否则程序报错
特点:
1.抽象类不可以创建对象
2.抽象类的子类,必须重写父类中的构建方法。否则,子类必须也是抽象类
作用:
因为子类必须重写父类的构造方法。所以可以通过抽象类可以强制的要求子类中必须有哪些方法。
小知识点:抽象类里面可以有正常的方法,不一定全是抽象的方法。但只要有一个抽象的方法,那么这个类就必须是一个抽象类
(3)代码:
//在类中如果有一个抽象方法,那么这个类就必须是一个抽象类
public abstract class Animal{
//abstract修饰方法,这个方法就是一个抽象方法,抽象方法没有方法体,直接分号结束
public abstract void eat();
}
子类里必须重写父类里边的构建方法:
public class Cat extends Animal{
public void eat(){
//抽象方法的子类,必须重写父类里边的构建方法
}
}
7.接口
(1)概念:
1.接口实际上是一种特殊的抽象类
2.接口中所有的方法都是抽象方法
3.接口使用interface来声明
(2)特点:
1.能继承接口的只能是接口
2.接口和类只能是实现关系 用implements
去继承接口
3.相同的,继承接口时也要实现父类接口的构造方法(重写和实现是一个意思,但为了区分接口和抽象类所以创建了实现这个新词)
4.接口也具有多态性: Valuable g = new Gold();
(3)使用:
类只能单继承,接口支撑多实现,其他方面接口和类一模一样。
定义接口的代码:
public interface Valuable{ //接口用interface来声明,它已经不再是类了
public abstract void getMoney();
//接口中所有的方法都是抽象方法,如果不加abstract则会报错,方法体也不能写
// 接口中所有的方法都是抽象方法,所以可以省略abstract
// 接口中所有的方法都是公开的,所以也可以省略public,也就是说前面可以啥也不写
void getMoney(); => 这样也是可以的
}
继承接口的代码
public class Gold implements Valuable{ // 只能用implements来继承接口
//继承抽象类时必须要重写父类的构造方法
//相同的,继承接口时也要实现父类接口的构造方法(重写和实现是一个意思,但为了区分接口和抽象类所以创建了实现这个新词)
public void getMoney(){
System.out.println("黄金可以换钱");
}
}
类只能但继承,但接口可以多实现:
//类可以继承一个类,实现多个接口
public class Panda excends Animal implenments Valuable, Protectable{
//多实现,熊猫继承了动物,熊猫还实现了它是一个值钱的东西、熊猫还实现了它是一个受保护的东西
}
因为继承一个类又实现了多个接口,所以要重写抽象方法时代码量很多,很容易出错,我们可以使用idea的快捷方法来快速重写父类或接口的抽象方法:
右键 => Generate… => Override Methods… => 把需要重写的内容选定点ok
(4)意义:
例如熊猫可以是一个值钱的东西,金子也可以是一个值钱的东西,但是熊猫只方便继承动物类,金子方便继承金属类。也就是说熊猫是一个动物和金子是一个金属完全是两样东西,我们可以通过接口,把很多完全不想关的东西进行了统一整合。
8.内存
(1)内存分析
1.堆, 主要存放对象 一般new出来的东西都存放在堆里
2.栈, 局部变量以及一些基本数据类型的变量
3.代码区, 放类和方法
4.数据区, 常量池(一般放的字符串)和静态变量
(2)参数传递的问题
值传递:把变量的值作为参数进行传递
引用传递:直接把变量作为参数进行传递
Java使用的的值传递,这个问题在Java里特别多,很重要!
9.补充
(1)成员变量初始值:
Java中所有的变量都必须先声明,后赋值才能使用。
Java中的成员变量,在创建对象的的时候,都会执行一次初始化操作,都会给一个默认值。
基本数据类型默认值都是0,包括boolean
值是 => false
引用数据类型是 => null
表示空,什么都没有,占位
(2)object
万事万物皆为对象,所有东西都是对象
在Java中所有的类都要继承object
object是一个类,所有类的根,我们写的类既使不写继承关系,那么也会默认继承object,也就是说Java中默认的方法就是object中的方法。
(3)equals和双等号==的区别
==: 用来判断左右两端的数据是否一致
equals: object类提供的一个方法,用来判断两个对象是否相等
System.out.println(c1 == c2);
–> false
双等号==默认判断的是两个对象的内存地址是否一致,若内存地址一样则返回真,否则都是false。换句话说双等号判断的是两只猫是否是同一只猫,而不是猫的颜色或名字一样。一般用在基本数据类型上。
System.out.println(c1.equals(c2));
-> 也是false
equals默认情况下和双等号没区别,但是equals可以自己在子类中重写。
困扰程序员的难题:
1.当使用字符串string来定义一个事物时,若两个事物一样,则两个会使用同一个内存空间,这是Java为了提高执行效率所做的改变,这是string跟基本数据类型不一样的地方。
此时因为两个字符串用的都是同一个内存地址,所以相同,打印出来的都是true
2.字符串string里面是重写了equals方法的,他只会判断两个字符串的内容是否一致。
而双等号判断的是地址是否相等,使用new创建对象会新建出一个字符串的空间,但它们指向的其实依旧是同一个小红。
所以字符串的判断一定要用equals来判断,它可以帮我们判断内容是否相等,我们使用字符串判断时,一般都是判断字符串的内容。
(4)toString方法
直接打印一个对象,相当于默认执行了toString方法
默认的toString()
返回的是 包名+类名@内存地址
(5)instanceof关键字
instanceof
:判断xxx对象是否是xxx类型的,就是用来判断类型的
例如当一只猫向上转型成动物时,我们就可以用instanceof
来判断它到底是不是一只猫:
public class Cat extends Animal{
public static void main(String[] args){
Animal ani = new Cat();
if(ani instanceof Cat){ //用instanceof来判断ani是否是一只猫
System.out.println("是一只猫");
}else{
System.out.println("不是一只猫");
}
}
}
10.异常
###### (1)异常处理
1.异常: 异常是运行时的错误,程序的编译是通过的,但是运行却出了异常
2.抛异常: 创建一个错误对象,把错误对象丢出来
3.捕获异常: 默认由JVM来把错误信息进行捕获,打印出来。JVM会终止程序的执行。
(2)异常分类
Throwable
(所有异常的根)
1.Error
(系统级的错误,一般出了Error都是大事)
2.Exception
(异常)
1.RuntimeException
:运行时异常
一大堆Exception
2.其他Exception
:正常的一个错误,必须手动去处理,不然编译器通不过
(3)try…catch
try{
尝试执行可能会出现异常的代码
}catch(Exception e){
处理异常的代码
}finally{
最终的,出不出错都要执行它,它可以把文件的一个打开状态直接关上,非常有用,建议使用。
否则不把文件打开状态关闭会一直占用内存空间。
}
(4)throws和throw
抛异常处理
1.throws表示方法准备要扔出来一个异常
产生的错误尽可能的自己处理,少向外抛出异常
2.throw表示向外抛出异常
throws:
public class Example {
public void readFile() throws IOException {
// 模拟文件读取操作,可能抛出IOException
throw new IOException("文件读取错误");
}
public static void main(String[] args) {
Example example = new Example();
try {
example.readFile(); // 需要处理IOException
} catch (IOException e) {
System.out.println("捕获到异常: " + e.getMessage());
}
}
}
throw:
public class Example {
public void checkAge(int age) {
if (age < 18) {
throw new IllegalArgumentException("年龄必须大于或等于18");
}
System.out.println("年龄符合要求");
}
public static void main(String[] args) {
Example example = new Example();
example.checkAge(16); // 这将抛出IllegalArgumentException
}
}
(5)自定义异常
在Java中,自定义异常类的创建通常是通过扩展现有的异常类来实现。自定义异常可以帮助你处理特定于应用程序的错误情况。以下是自定义异常的基本语法和示例。
- 创建一个类:创建一个新类,用
extends
关键字继承自Exception
类(或其子类),以创建检查型异常;如果不需要检查型异常,可以继承自RuntimeException
类。 - 构造方法:为异常类添加构造方法,通常包括无参构造、包含错误信息的构造,以及包含错误信息和错误原因的构造。
下面是一个简单的自定义异常类的示例:
javaCopy Code// 自定义异常类
public class CustomException extends Exception {
// 无参构造
public CustomException() {
super("默认错误信息");
}
// 带错误信息的构造
public CustomException(String message) {
super(message);
}
// 带错误信息和原因的构造
public CustomException(String message, Throwable cause) {
super(message, cause);
}
}
创建自定义异常后,可以在方法中抛出该异常,并在调用该方法时处理它。
javaCopy Codepublic class Example {
// 一个可能抛出自定义异常的方法
public void doSomething(int value) throws CustomException {
if (value < 0) {
throw new CustomException("值不能小于0");
}
System.out.println("值是: " + value);
}
public static void main(String[] args) {
Example example = new Example();
try {
example.doSomething(-1); // 这将抛出CustomException
} catch (CustomException e) {
System.out.println("捕获到异常: " + e.getMessage());
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端