文章目录
🧱🧱🧱面向对象编程(oop)
开发一个一个的对象,把数据交给对象,再调用对象的方法完成对数据的处理
package object;
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "girl";
s1.math = 100;
s1.chinese = 99;
s1.printTotalScore();
s1.printAverageScore();
Student s2= new Student();
s2.name = "boy";
s2.chinese = 59;
s2.math = 98;
s2.printTotalScore();
s2.printAverageScore();
}
}
package object;
public class Student {
//成员变量(对象的属性)
String name;
double chinese;
double math;
//成员方法(对象的行为)
public void printTotalScore() {
System.out.println(name + ":总成绩为:" + (chinese + math));
}
public void printAverageScore() {
System.out.println(name + ":平均成绩为:" + (chinese + math) / 2.0);
}
}
//响应结果:
girl:总成绩为:199.0
girl:平均成绩为:99.5
boy:总成绩为:157.0
boy:平均成绩为:78.5
Process finished with exit code 0
程序中的对象本质上是一种特殊的数据结构。
class就是类,也称为对象的设计图(或者对象的模板)
先用class设计对象,然后用class new出对象,new几个程序里就有多少张表(有多少个对象)
面向:拿或者是找
对象:东西
面向对象编程:拿或者找东西过来编程解决问题
面向对象:把现实世界中的事物全部看成一个一个的对象来解决问题(万物皆对象)
面向对象的好处:更符合人类的思维习惯,编程程序更简单,看程序更容易理解
类(设计图):相同事物共同特征的描述
对象:对象是类的具体实例
在java中必须要先定义类,才能得到对象
类中:一般名词定义成员变量(属性);一般动词定义成员方法(行为)
对象在计算机中的执行原理
main方法是提到栈内存中执行的
每次new Student(),就是在堆内存中开辟一块内存区域代表一个学生对象。
s1变量里面记住的是学生对象的地址
- 如何识别引用类型的变量? s1变量中存储的是对象的地址,因此变量s1也称为引用类型的变量。
类和对象的一些注意事项
-
类名建议用英文单词,首字母大写,满足驼峰模式,且要有意义,比如:Student、Car…
-
类中定义的变量也称为成员变量(对象的属性),类中定义的方法也称为成员方法(对象的行为)。
-
成员变量本身存在默认值,同学们在定义成员变量时一般来说不需要赋初始值(没有意义)。
-
一个代码文件中,可以写多个class类,但只能一个用public修饰,且public修饰的类名必须成为代码文件名。
-
对象与对象之间的数据不会相互影响,但多个变量指向同一个对象时就会相互影响了(引用类型,地址覆盖)。
-
如果某个对象没有一个变量引用它,则该对象无法被操作了,该对象会成为所谓的垃圾对象。
-
当堆内存中的对象,没有被任何变量引用(指向)时,就会被判定为内存中的“垃圾”,但是不用管它,因为java存在自动垃圾回收机制,会自动清除垃圾对象。
this
this就是一个变量,可以用在方法里,来拿到当前对象;哪个对象调用方法,this就指向哪个对象,也就是拿到哪个对象。
this的应用场景:主要用来解决对象的成员变量与方法内部变量名称冲突的问题的。
package object;
public class Student {
double score; //学生成绩
public void printPass(double score){
//用来判断学生的成绩是否超过学校的分数线
if (this.score> score){ //这里this.score的this指的是上面的学生成绩
System.out.println("恭喜您成功考进学校了");
} else {
System.out.println("您落选了");
}
}
}
package object;
public class Test {
public static void main(String[] args) {
Student s3 = new Student();
s3.score = 325;
s3.printPass(300);
}
}
//相应结果:
恭喜您成功考进学校了
Process finished with exit code 0
构造器
构造器用来干什么?
对象创建时,我们可以指定对象去调用哪个构造器执行。
构造器常用于完成对象初始化
public class Student{
public Student(){
...
}
}
构造器特点
创建对象时,对象会去调用构造器。
package constructor;
public class Student {
//无参数构造器
public Student(){
System.out.println("无参数构造器被触发执行了~");
}
//有参数构造器
public Student(String name, double score){
System.out.println("有参数构造器被触发执行了~");
}
}
package constructor;
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student("张三",98);
}
}
//相应结果:
无参数构造器被触发执行了~
有参数构造器被触发执行了~
Process finished with exit code 0
常见应用场景
创建对象时,同时完成对对象成员变量(属性)的初始化赋值。
注意事项
- 类在设计时,如果不写构造器,java是会为类自动生成一个无参构造器的。
- 一旦定义了有参数构造器,java就不会帮我们的类自动生成无参构造器了,此时建议自己手写一个无参数构造器
🧱面向对象三大特征之一:封装
面向对象的三大特征:封装、继承、多态
什么是封装?
就是用类设计对象处理某一个事物的数据时,应该把处理的数据,以及处理这些数据的方法,设计到一个对象中去。
封装的设计规范
合理隐藏(对象里的部分成员不应该被暴露出来)、合理暴露
private是对外隐藏
代码层面如何控制对象的成员公开或隐藏?
为了系统的安全性,可以将对象内部的成员变量设置为private,将对象中的方法设置为public,仅对外暴露相应的方法即可。而且一般会设置两个方法,get和set
package constructor;
public class Student {
private double score;
public void setScore(double score) {
if (score >= 0 && score <= 100){ //控制赋值是否合理
this.score = score;
} else {
System.out.println("数据非法!");
}
}
public double getScore() {
return score;
}
public void printPass() {
System.out.println("结果为:");
}
}
package constructor;
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.setScore(99);
System.out.println(s.getScore());
}
}
//响应结果:
99.0
Process finished with exit code 0
实体JavaBean(实体类)
一种特殊形式的类
实体类就是一种特殊形式的类。
- 这个类中的成员变量都要私有,并且要对外提供相应的getXxx, setXxx方法。
- 类中必须要有一个公共的无参的构造器
package constructor;
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("波妞");
s1.setScore(99);
System.out.println(s1.getName());
System.out.println(s1.getScore());
}
}
package constructor;
public class Student {
//1 这个类中的成员变量都要私有,并且要对外提供相应的getXxx, setXxx方法。
private String name;
private double score;
//2 类中必须要有一个公共的无参的构造器
public Student() {
}
public Student(String name, double score) {
this.name = name;
this.score = score;
}
//get、set
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
//响应结果:
波妞
99.0
Process finished with exit code 0
常见应用场景
实体类只负责数据的存取,而对数据的处理交给其他类来完成,以实现数据和数据业务处理相分离。
package constructor;
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("波妞");
s1.setScore(99);
System.out.println(s1.getName());
System.out.println(s1.getScore());
StudentOperator operator = new StudentOperator(s1);
operator.printPass();
}
}
package constructor;
public class Student {
//1 这个类中的成员变量都要私有,并且要对外提供相应的getXxx, setXxx方法。
private String name;
private double score;
//2 类中必须要有一个公共的无参的构造器
public Student() {
}
public Student(String name, double score) {
this.name = name;
this.score = score;
}
//get、set
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
package constructor;
public class StudentOperator {
private Student student;
public StudentOperator(Student student) {
this.student = student;
}
public void printPass() {
if (student.getScore() >= 60) {
System.out.println(student.getName() + "学生成绩及格!");
} else {
System.out.println(student.getName() + "学生成绩不及格!");
}
}
}
//响应结果:
波妞
99.0
波妞学生成绩及格!
Process finished with exit code 0
弹幕:一个用来存取对象的数据,一个操作对象去实行业务逻辑,最后主页用定义的实体对象去调用需要的业务逻辑方法就可以了,也就是说我只需要改变业务逻辑和修改数据就可以了。
javabean(实体类)
private 变量;
public 无参构造器;
public 有参构造器;
public get set;
Operator(业务)
private Student student;
public 有参构造器;
public 业务;
main(主函数)
Student s1 = new Student();
s1.set
Operator op = new Operator(s1);
op.业务;
面向对象编程综合案例(模仿电影信息系统)
需求:
- 展示系统中的全部电影(每部电影展示:名称、价格)
- 允许用户根据电影编号(id)查询出某个电影的详细信息
目标:
- 使用所学的面向对象编程实现以上两个需求
用数组传递电影全部信息
package demo;
public class Test {
public static void main(String[] args) {
//1.准备所有的电影
Movie[] movies = new Movie[4];
movies[0] = new Movie(1, "水门桥", 5.0, 39.2, "徐克", "吴京", "12万人想看");
movies[1] = new Movie(2, "出拳吧", 7.8, 39, "唐晓白", "田雨", "3.5万人想看");
movies[2] = new Movie(3, "月球陨落", 7.9, 42, "罗兰", "贝瑞", "4万人想看");
movies[3] = new Movie(4, "一点就到家", 8.7, 35, "许宏宇", "刘昊然", "7万人想看");
//2.创建一个电影操作类的对象,接收电影数据,并对其进行业务处理
Operator op = new Operator(movies);
op.printAllMovie();
op.searchMovieById(1);
}
}
=====================================================================================
package demo;
public class Movie {
private int id;
private String name;
private double score;
private double price;
private String director;
private String star;
private String comment;
public Movie() {
}
public Movie(int id, String name, double score, double price, String director, String star, String comment) {
this.id = id;
this.name = name;
this.score = score;
this.price = price;
this.director = director;
this.star = star;
this.comment = comment;
}
public int getId(){
return id;
}
public void setId(int id){
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getDirector() {
return director;
}
public void setDirector(String director) {
this.director = director;
}
public String getStar() {
return star;
}
public void setStar(String star) {
this.star = star;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}
=====================================================================================
package demo;
public class Operator {
private Movie[] movies;
public Operator(Movie[] movies) {
this.movies = movies;
}
//展示全部电影信息
public void printAllMovie(){
System.out.println("---全部电影信息如下:---");
for (int i = 0; i < movies.length; i++) {
Movie m =movies[i];
System.out.println("编号:" + m.getId());
System.out.println("名称为:" + m.getName());
System.out.println("价格:" + m.getPrice());
System.out.println("------------");
}
}
//根据电影编号查询出该电影的详细信息并展示
public void searchMovieById(int id){
for (int i = 0; i < movies.length; i++) {
Movie m = movies[i];
if (m.getId() == id){
System.out.println("----电影详情为:----");
System.out.println("编号:" + m.getId());
System.out.println("名称为:" + m.getName());
System.out.println("价格:" + m.getPrice());
System.out.println("导演信息:" + m.getDirector());
return; //表示已经找到了电影信息,没有必要再执行了
}
}
System.out.println("没有该电影!!");
}
}
升级版(可根据输入信息执行相应的操作)
package demo;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
//1.准备所有的电影
Movie[] movies = new Movie[4];
movies[0] = new Movie(1, "水门桥", 5.0, 39.2, "徐克", "吴京", "12万人想看");
movies[1] = new Movie(2, "出拳吧", 7.8, 39, "唐晓白", "田雨", "3.5万人想看");
movies[2] = new Movie(3, "月球陨落", 7.9, 42, "罗兰", "贝瑞", "4万人想看");
movies[3] = new Movie(4, "一点就到家", 8.7, 35, "许宏宇", "刘昊然", "7万人想看");
//2.创建一个电影操作类的对象,接收电影数据,并对其进行业务处理
Operator op = new Operator(movies);
// op.printAllMovie();
// op.searchMovieById(1);
while (true) {
System.out.println("请输入要执行的操作:");
System.out.println("1.展示所有电影信息");
System.out.println("2.查询电影信息");
Scanner scanner = new Scanner(System.in); //如果担心占内存,可以放到循环外面
int command = scanner.nextInt();
switch (command){
case 1:
op.printAllMovie();
break;
case 2:
System.out.println("输入要查询的电影Id:");
Scanner scanner2 = new Scanner(System.in);
int command2 = scanner2.nextInt();
op.searchMovieById(command2);
break;
default:
System.out.println("输入的命令不对!");
}
}
}
}
//
请输入要执行的操作:
1.展示所有电影信息
2.查询电影信息
3
输入的命令不对!
请输入要执行的操作:
1.展示所有电影信息
2.查询电影信息
2
输入要查询的电影Id:
2
----电影详情为:----
编号:2
名称为:出拳吧
价格:39.0
导演信息:唐晓白
请输入要执行的操作:
1.展示所有电影信息
2.查询电影信息
成员变量、局部变量的区别
常用API(Application Programming Interface: 应用程序编程接口)
不要重复造轮子——>API文档
包
包是分门别类管理各种不同程序的,类似于文件夹,建包有利于程序的管理和维护。
别人写好的程序通常都是在别人的包里。
package com.it.javabean;
在自己程序中调用其他包下的程序的注意事项
- 如果当前程序中,要调用自己所在包下的其他程序,可以直接调用。(同一个包下的类,互相可以直接调用)
- 如果当前程序中,要调用其他包下的程序,必须在当前程序中导包,才可以访问!
导包格式:import 包名.类名; - 如果当前程序中,要调用Java提供的程序,也需要先导包才可以使用;但是Java.lang包下的程序是不需要我们导包的,可以直接使用。
- 如果当前程序中,要调用多个不同包下的程序,而这些程序名正好一样,此时默认只能导入一个程序,另一个程序必须带包名访问。
String
String创建对象封装字符串数据的方式
String代表字符串,可以用来创建对象封装字符串数据,并对其进行处理。
- 方式一:java程序中的所有字符串文字(例如“abc”)都为此类的对象。【直接使用双引号】
String name="小黑";
- 方式二:调用String类的构造器初始化字符串对象【new String类,调用构造器初始化字符串对象】
构造器 | 说明 |
---|---|
public String() | 创建一个空白字符串的对象,不含有任何内容 |
public String(String original) | 根据传入的字符串内容,来创建字符串对象 |
public String(char[] chars) | 根据字符数组的内容,来创建字符串对象 |
public String(byte[] bytes) | 根据字节数组的内容,来创建字符串对象 |
public class Demo1 {
public static void main(String[] args) {
//1.直接双引号得到字符串,封装字符串数据
String name = "itheima";
System.out.println(name);
//2.new String创建字符串对象,并调用构造器初始化字符串
String rs1 = new String();
System.out.println(rs1);
String rs2 = new String("itheima");
System.out.println(rs2);
char[] chars = {'a', '黑', '马'};
String rs3 = new String(chars);
System.out.println(rs3);
byte[] bytes = {97, 98, 99};
String rs4 = new String(bytes);
System.out.println(rs4);
}
}
//响应结果:
itheima
itheima
a黑马
abc
Process finished with exit code 0
String常用方法
public class Demo2 {
public static void main(String[] args) {
String s = "黑马java";
//1.获取字符串长度
System.out.println(s.length());
//2.提取字符串中某个索引位置处的字符,索引从0开始
char c = s.charAt(1);
System.out.println(c);
//字符串的遍历
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
System.out.println(ch);
}
System.out.println("===========");
//3.把字符串转换成字符数组再进行遍历
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
System.out.println(chars[i]);
}
System.out.println("===========");
//4.判断字符串内容,内容一样就返回true
String s1 = new String("黑马");
String s2 = new String("黑马");
System.out.println(s1 == s2); //false 因为这样比较的是地址
System.out.println(s1.equals(s2)); //true 这样比较的是内容
System.out.println("===========");
//5.忽略大小写,比较字符串内容(验证码)
String c1 = "34AeFG";
String c2 = "34aEfg";
System.out.println(c1.equals(c2)); //false
System.out.println(c2.equalsIgnoreCase(c2)); //true
System.out.println("===========");
//6.截取字符串内容(包前不包后)
String s3 = "java是最好的编程语言之一"; //截取j-编,看IDEA右下角-1
String rs = s3.substring(0,8);
System.out.println(rs);
System.out.println("===========");
//7.从当前索引位置一直截取到字符串的末尾
String rs2 = s3.substring(5);
System.out.println(rs2);
System.out.println("===========");
//8.把字符串中的某个内容替换成新内容,并返回新的字符串对象给我们(替换敏感语言)
String info = "这个电影简直是垃圾,垃圾电影!!";
String rs3 = info.replace("垃圾","**");
System.out.println(rs3);
System.out.println("===========");
//9.判断字符串中是否包含某个关键字,区分大小写!
String info2 = "java是最好的编程语言之一,我爱java,java不爱我!";
System.out.println(info2.contains("java"));
System.out.println("===========");
//10.判断字符串是否以某个字符串开头(判断某个人的姓氏)
String rs4 = "张三丰";
System.out.println(rs4.startsWith("张"));
System.out.println(rs4.startsWith("张三"));
System.out.println(rs4.startsWith("张三2"));
System.out.println("===========");
//11.把字符串按照某个指定内容分割成多个字符串,放到一个字符串数组中返回给我们(在网页中选择信息送给后台)
String rs5 = "张无忌,周芷若,殷素素,赵敏";
String[] names = rs5.split(",");
for (int i = 0; i < names.length; i++) {
System.out.println(names[i]);
}
}
}
//响应结果
6
马
黑
马
j
a
v
a
===========
黑
马
j
a
v
a
===========
false
true
===========
false
true
===========
java是最好的
===========
最好的编程语言之一
===========
这个电影简直是**,**电影!!
===========
true
===========
true
true
false
===========
张无忌
周芷若
殷素素
赵敏
Process finished with exit code 0
String使用时的注意事项(笔试题)
1. String对象的内容不可改变,被称为不可变字符串对象。
结论:每次试图改变字符串对象实际上是新产生了新的字符串对象,变量每次都是指向了新的字符串对象,之前字符串对象的内容确实是没有改变的,因此说String的对象是不可变的。
2. 只要是以"…"方式写出的字符串对象,会存储到字符串常量池,且相同内容的字符串只会存储一份;但通过new 方式创建字符串对象,每new一次都会产生一个新的对象放在新的对象放在堆内存中。
public class Demo3 {
public static void main(String[] args) {
//1.只要是以"..."方式写出的字符串对象,会存储到字符串常量池,且相同内容的字符串只会存储一份
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);
//2.new String创建字符串对象,每次new出来的都是一个新对象,放在堆内存中
char[] chars = {'a', 'b', 'c'};
String a1 = new String(chars);
String a2 = new String(chars);
System.out.println(a1 == a2);
}
}
//响应结果:
true
false
Process finished with exit code 0
一道笔试题(创建了几个对象)
public class Demo4 {
public static void main(String[] args) {
String s2 = new String("abc");
/*这行代码创建了几个对象? 2个
①因为双引号是放在常量池里的,常量池会产生一个字符串对象。
②new又产生一个字符串,放在堆里*/
String s1 = "abc";
/*这行代码创建了几个对象? 0个
因为创建abc的时候,因为abc是双引号给出来的,会放在常量池中,但是常量池中已经有了,所以不会创建新对象,而是直接指向*/
System.out.println(s1 ==s2);
//false 因为s1指向的是常量池中的abc,而s2指向的是堆内存的abc,地址不同
}
}
另一道笔试题(程序打印)
阅读以下两个程序,请写出打印的结果
public class Demo4 {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "ab";
String s3 = s2 + "c";
System.out.println(s1 == s3); //false,因为s3是运算出来的,运算是在堆内存中
}
}
public class Demo4 {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "a" + "b" + "c";
System.out.println(s1 == s2); //true
//java存在编译优化机制,程序在编译时:"a" +"b"+"c"会直接转成"abc",以提高程序的执行性能
}
}
String的应用案例
完成用户登录
系统正确的登录名和密码是:itheima/123456,请在控制台开发一个登录界面,接受用户输入的登录名和密码,判断用户是否登录成功,登录成功后展示:“欢迎进入系统!”,即可停止程序
(注意:要求最多给用户三次登录机会)
步骤:
1、开发登录界面,提示用户通过键盘输入登录名和密码。
2、设计一个登录方法,对用户的登录名和密码进行正确性认证。
3、根据登录方法返回的认证结果,判断用户是否登录成功。
4、使用循环控制登录界面最多显示3次。
import java.util.Scanner;
public class Demo5 {
public static void main(String[] args) {
//1.开发一个登录界面
for (int i=0; i<3; i++) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String loginName = sc.next();
System.out.println("请输入密码:");
String loginPwd = sc.next();
//5.开始调用登陆方法,判断是否登陆成功
boolean rs = login(loginName, loginPwd);
if (rs) {
System.out.println("欢迎进入系统!");
break;
} else {
System.out.println("错误重试");
}
}
}
//2.开发一个登录方法,接收用户的登录名和密码,返回认证的结果
public static boolean login(String loginName, String loginPwd) {
//3.准备一份系统正确的登录名和密码
String okLoginName = "itheima";
String okLoginPwd = "123456";
//4.开始正式判断用户是否登录成功
if (okLoginName.equals(loginName) && okLoginPwd.equals(loginPwd)) {
//登陆成功
return true;
} else {
//登陆失败
return false;
//4.替换优雅写法
return okLoginName.equals(loginName) && okLoginPwd.equals(loginPwd);
}
}
}
使用String来开发验证码
实现随机产生验证码,验证码的每位可能是数字、大写字母、小写字母。
- 设计一个方法,该方法接收一个整型参数,最终要返回对应位数的随机验证码。
- 方法内定义2个字符串变量:1个用来记住生成的验证码,1个用来记住要用到的全部字符。
String code = “”;
String data = “abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789”; - 定义for循环控制生成多少位随机字符,每次得到一个字符范围内的随机索引,根据索引提取该字符
,把该字符交给code变量连接起来,循环结束后,在循环外返回code即可。 - 主程序中,调用该方法即可得到随机验证码了。
import java.util.Random;
public class Demo6 {
public static void main(String[] args) {
System.out.println(createCode(4));
}
//1.设计一个方法,返回指定位数的验证码
public static String createCode(int n){
//2.定义2个变量,一个是记住最终产生的验证码,一个是记住可能用到的全部字符
String code = "";
String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random r = new Random();
//3.开始定义一个循环产生每位随机字符
for (int i = 0; i < n; i++) {
//4.随机一个字符范围内的索引
int index = r.nextInt(data.length());
//5.根据索引去全部字符中提取字符
code += data.charAt(index);
}
//6、返回code即可
return code;
}
}
ArrayList
什么是集合? 集合是一种容器,用来装数据的,类似于数组
但是数组:
-
数组定义完成启动后,长度就固定了
-
集合大小可变,开发中用的更多
ArrayList集合要学习什么:
- 会提供创建容器对象的方式(创建对象)
- 会提供相应的方法对容器进行操作(增删改查)
- 容器的其他特点
import java.util.ArrayList;
public class ArrayListDemo1 {
public static void main(String[] args) {
//1.创建一个ArrayList的集合对象
//ArrayList<String> list999 = new ArrayList<String>(); 用这种方式限制输入的类型(从jdk1.7才开始支持)
ArrayList<String> list = new ArrayList();
list.add("黑马");
list.add("java");
list.add("黑马1");
list.add("黑马2");
list.add("黑马3");
System.out.println(list); //存的是地址,但是会打印出内容
//2.往集合中的某个索引位置处添加一个数据
list.add(1,"mysql");
System.out.println(list);
//3.根据索引获取集合中某个索引位置处的值
String rs = list.get(1);
System.out.println(rs);
//4.获取集合的大小,返回集合中存储的元素个数
System.out.println(list.size());
//5.根据索引删除集合中的某个元素值,会返回被删除的元素值给我们
System.out.println(list.remove(1));
System.out.println(list);
//6.直接删除某个元素值,删除成功会返回true,反之false
System.out.println(list.remove("java"));
System.out.println(list.remove("黑马")); //默认删第一个符合的数据
System.out.println(list);
//7.修改某个索引位置处的数据,修改后会返回原来的值给我们
System.out.println(list.set(1, "programmer"));
System.out.println(list);
}
}
//
[黑马, java, 黑马1, 黑马2, 黑马3]
[黑马, mysql, java, 黑马1, 黑马2, 黑马3]
mysql
6
mysql
[黑马, java, 黑马1, 黑马2, 黑马3]
true
true
[黑马1, 黑马2, 黑马3]
黑马2
[黑马1, programmer, 黑马3]
Process finished with exit code 0
ArrayList应用案例
掌握从容器中找出某些数据并成功删除的技巧
需求:
现在假如购物车中存储了如下这些商品:Java入门,宁夏枸杞,黑枸杞,人字拖,特级枸杞,枸杞子。现在用户不想买枸杞了,选择了批量删除,请完成该需求。
分析:
- 后台使用ArrayList集合表示购物车,存储这些商品名。
- 遍历集合中的每个数据,只要这个数据包含了“枸杞”则删除它。
- 输出集合看是否已经成功删除了全部枸杞数据了。
import java.util.ArrayList;
public class ArrayListDemo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("java入门");
list.add("宁夏枸杞");
list.add("黑枸杞");
list.add("人字拖");
list.add("特级枸杞");
list.add("枸杞子");
System.out.println(list);
for (int i = 0; i < list.size(); i++) {
String ele = list.get(i);
if (ele.contains("枸杞")){
list.remove(ele);
}
}
System.out.println(list);
}
}
//
[java入门, 宁夏枸杞, 黑枸杞, 人字拖, 特级枸杞, 枸杞子]
[java入门, 黑枸杞, 人字拖, 枸杞子]
Process finished with exit code 0
以上代码没有满足需求的原因是元素是一个一个删掉的,删掉后落查了。
先删除,索引后往后移
解决方案:
//方式一:(每次删除一个数据)
for (int i = 0; i < list.size(); i++) {
String ele = list.get(i);
if (ele.contains("枸杞")){
list.remove(ele);
i--;
}
}
//方式二:(从集合的后面倒着遍历并删除)
for (int i = list.size() -1 ; i >=0; i--) {
String ele = list.get(i);
if (ele.contains("枸杞")) {
list.remove(ele);
}
}
ArrayList综合案例
模仿外卖系统中的商家系统
需求:完成菜品的上架、以及菜品信息浏览功能。
目标:使用所学的ArrayList集合结合面向对象编程实现以上2个需求。
//main
package tuozhan;
import java.util.ArrayList;
public class ArrayListDemo3 {
public static void main(String[] args) {
FoodOperator fo = new FoodOperator();
fo.start();
}
}
//operator
package tuozhan;
import java.util.ArrayList;
import java.util.Scanner;
public class FoodOperator {
//1.定义一个ArrayList集合对象,负责存储菜品对象信息
private ArrayList<Food> foodList = new ArrayList<>();
//2.上架菜品功能
public void addFood(){
//3.创建一个菜品对象,封装上架的菜品信息
Food f =new Food();
//4.录入菜品信息进去
Scanner sc = new Scanner(System.in);
System.out.println("输入菜品名字:");
String name = sc.next();
f.setName(name);
System.out.println("输入菜品价格:");
double price = sc.nextDouble();
f.setPrice(price);
System.out.println("输入菜品描述:");
String desc = sc.next();
f.setDesc(desc);
//5.把菜品对象存入集合中
foodList.add(f);
System.out.println("success");
}
//6.展示菜品
public void showAllFood(){
if (foodList.size() == 0){
System.out.println("无菜品,去添加");
return;
}
for (int i = 0; i < foodList.size(); i++) {
Food f = foodList.get(i);
System.out.println(f.getName());
System.out.println(f.getPrice());
System.out.println(f.getDesc());
System.out.println("-------");
}
}
//负责展示操作界面
public void start(){
while (true) {
System.out.println("请选择功能:");
System.out.println("1.上架菜品");
System.out.println("2.展示菜品");
System.out.println("3.exit!");
Scanner sc = new Scanner(System.in);
String command = sc.next();
switch (command){
case "1":
addFood();
break;
case "2":
showAllFood();
break;
case "3":
System.out.println("see you");
return;
default:
System.out.println("输入命令不存在");
}
}
}
}
//javabean
package tuozhan;
public class Food {
private String name;
private double price;
private String desc;
public Food() {
}
public Food(String name, double price, String desc) {
this.name = name;
this.price = price;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
oop高级
static
static修饰成员变量
static称为静态,可以修饰成员变量、成员方法。
成员变量按照有无static修饰,分为两种:
- 类变量:有static修饰,属于类,与类一起加载一次,在计算机内存中只有一份,会被类的全部对象共享
- 实例变量(对象的变量):无static修饰,属于每个对象的,每个对象都有一份
public class Student{
//类变量
static String name; //有static修饰,属于类,在计算机里只有一份,会被类的全部对象共享
//实例变量(对象变量)
int age; //无static修饰,属于每个对象的
}
一个学生可以有多个成绩,而这些成绩都属于这个学生
表达方式:
类名.类变量(推荐)
对象.类变量(不推荐)
package d1staticdemo;
public class Test {
public static void main(String[] args) {
//1.类变量的用法
//类名.类变量
Student.name="袁华"; //推荐的表达
//对象.类变量 可以,但不推荐
Student s1 = new Student();
s1.name = "马冬梅";
Student s2 = new Student();
s2.name = "秋雅";
System.out.println(s1.name); //秋雅
System.out.println(Student.name); //秋雅
//2.实例变量的用法,属于每个对象的变量
s1.age = 23;
s2.age = 18;
System.out.println(s1.age); //23
System.out.println(s2.age); //18
// System.out.println(Student.age); 报错,因为不知道你要选哪个对象
}
}
package d1staticdemo;
public class Student {
//类变量
static String name;
//实例变量(对象变量)
int age;
}
static修饰成员变量的应用场景
在开发中,如果某个数据只需要一份,且希望能够被共享(访问、修改),则该数据可以定义成类变量来记住。
记录用户类自己创建了多少个用户对象
public class User {
//类变量
public static int number;
//构造器(利用无参构造器记录)
public User(){
User.number++;
//这里也可以写成number++
//但是要注意,在同一个类中,访问自己类的类变量,才可以省略类名不写!
}
}
package d1staticdemo;
public class Test2 {
public static void main(String[] args) {
User u1 = new User();
User u2 = new User();
User u3 = new User();
User u4 = new User();
System.out.println(User.number);
}
}
//
4
static修饰成员方法
成员方法的分类:
-
类方法:有static修饰的成员方法,属于类。
public static void printHello(){ sout("hello"); }
类名.类方法(推荐)
对象名.类方法(不推荐)
-
实例方法:无static修饰的成员方法,属于对象。
public void printPass(){ ... }
package d2staticdemo;
public class Student {
double score;
public static void printHello() {
System.out.println("Hello world!");
System.out.println("Hello world!");
}
//实例方法(对象方法)
public void printPass() {
System.out.println("成绩:" + (score >= 60 ? "及格" : "不及格"));
}
}
package d2staticdemo;
public class Test {
public static void main(String[] args) {
//1.类方法的用法
//类名.类方法(推荐)
Student.printHello();
//对象名.类方法(不推荐)
Student s = new Student();
s.printHello();
//2.实例方法的用法
//对象.实例方法
s.printPass();
//类.实例方法——报错!!
Student.printPass(); //报错
}
}
执行原理:
– | – |
---|---|
类变量 | 静态成员变量 |
实例变量 | 对象变量 |
类方法 | 静态方法 |
实例方法 | 对象方法 |
补充:搞懂main方法
- main方法是什么方法? 类方法
- main方法是怎么直接跑起来的? 通过Test.main(…)
public class Test {
public static void main(String[] args){
...
}
}
3.String[] args 用来接数据
总结:静态是为了在只知道类名的情况下调用,方法名叫main是虚拟机固定调用这个名字的方法
static修饰成员方法的应用场景
类方法常用的应用场景是做工具类。
工具类中的方法都是一些类方法,每个方法都是用来完成一个功能的,工具类是给开发人员共同使用的。
使用工具类的好处:提高代码复用;调用方便,提高开发效率。每个类都可以用来完成一个功能
做一个验证码工具类
要求登录界面有4位验证码,注册界面有6位验证码
package d3staticdemo;
import java.util.Random;
public class MyUtil {
public static String createCode(int n){
String code = "";
String data="qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
Random r = new Random();
for (int i = 0; i < n; i++) {
int index = r.nextInt(data.length());
code += data.charAt(index);
}
return code;
}
}
package d3staticdemo;
public class LoginDemo {
public static void main(String[] args) {
System.out.println(MyUtil.createCode(4));
}
}
package d3staticdemo;
public class RegisterDemo {
public static void main(String[] args) {
System.out.println(MyUtil.createCode(6));
}
}
- Q:为什么工具类中的方法要用类方法,而不用实例方法?
A:实例方法需要创建对象类调用,此时对象只是为了调用方法,对象占内存,这样会浪费内存
A:若使用类方法,直接用类名调用即可,调用方便,也能节省内存
多学一招(私有构造器):
工具类没有创建对象的需求,建议将工具类的构造器进行私有
public class MyUtil {
private MyUtil() { //这样就不会用对象.方法名的方式调用了,从而避免内存浪费的问题
}
}
public class LoginDemo {
public static void main(String[] args) {
System.out.println(MyUtil.createCode(4));
MyUtil mu = new MyUtil(); //报错
mu.createCode(3); //报错
}
}
static的注意事项
使用类方法、实例方法时的注意事项:
类方法中可以直接访问类的成员,不可以直接访问实例成员。
实例方法中既可以直接访问类成员,也可以直接访问实例成员。
实例方法中可以出现this关键字,类方法中不可以出现this关键字。
package d4staticdemo;
public class Student {
static String schoolName; //🔸类变量
double score; //🔸实例变量
//🔸类方法
public static void printHello2(){
}
//🔸实例方法
public void printPass2(){
}
//➡️1.类方法中可以直接访问类的成员,不可以直接访问实例成员。
public static void printHello(){
Student.schoolName = "heima";
Student.printHello2();
//注意:同一个类中,访问类成员,可以省略类名不写
schoolName = "heimama";
printHello2();
// System.out.println(score); 报错
// printPass(); 报错
//➡️3.实例方法中可以出现this关键字,类方法中不可以出现this关键字。
// System.out.println(this); 报错
}
//➡️2.实例方法中既可以直接访问类成员,也可以直接访问实例成员
public void printPass(){
schoolName = "heima2";
printHello2();
System.out.println(score);
printPass2();
//➡️3.实例方法中可以出现this关键字,类方法中不可以出现this关键字。
System.out.println(this);
}
}
static的应用知识:代码块
代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类)
分为两种:
-
静态代码块:
格式:static{ }
特点:类加载是自动执行,由于类只会加载一次,所以静态代码块也只会执行一次。
作用:完成类的初始化,例如:对类变量的初始化赋值。
-
实例代码块:
格式:{ }
特点:每次创建对象时,执行实例代码块,并在构造器前执行。
作用:和构造器一样,都是用来完成对象的初始化的,例如:对实例变量进行初始化赋值。
package d5blockdemo;
public class Student {
static int num = 80;
static String schoolName;
//➡️静态代码块
static {
System.out.println("静态代码块执行了");
schoolName = "heima";
}
int age;
//➡️实例代码块(用的少,一般用于记录对象创建的日志)
{
System.out.println("实例代码块执行了");
// age = 18; 可以但没意义
}
public Student(){
System.out.println("无参构造器执行了");
}
public Student(String name){
System.out.println("有参构造器执行了");
}
}
package d5blockdemo;
public class Test {
public static void main(String[] args) {
System.out.println(Student.num);
System.out.println(Student.num);
System.out.println(Student.num);
System.out.println(Student.schoolName);
System.out.println("----------");
Student s1 = new Student();
Student s2 = new Student("张三");
}
}
//
静态代码块执行了
80
80
80
heima
----------
实例代码块执行了
无参构造器执行了
实例代码块执行了
有参构造器执行了
Process finished with exit code 0
static的应用知识:单例设计模式(架构师必会,用于开发框架)
设计模式(Design pattern):一个问题通常有n种解法,其中肯定有以一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。
设计模式有20多种,对应20多种软件开发中会遇到的问题。
关于设计模式主要学什么?1.解决什么问题?2.怎么写?
单例设计模式(饿汉式)⬅️用的频繁用它
确保一个类中只有一个对象。
写法:
- 把类的构造器私有。
- 定义一个类变量存储类的一个对象。
- 定义一个类方法,返回对象。
package d6singleInstance;
public class A {
// 2.定义一个类变量存储类的一个对象
private static A a = new A();
// 1.把类的构造器私有
private A(){
}
// 3.定义一个类方法,返回对象
public static A getObject(){
return a;
}
}
package d6singleInstance;
public class Test1 {
public static void main(String[] args) {
A a1 = A.getObject();
A a2 = A.getObject();
System.out.println(a1);
System.out.println(a2);
}
}
//
d6singleInstance.A@b0e86879
d6singleInstance.A@b0e86879
Process finished with exit code 0
单例模式可以理解为只需要一个对象的场景,确保每次类变量都是一个固定的对象,就像任务管理器每次打开都是同一个
单例设计模式的实现方式很多:
-
饿汉式单例:拿对象时,对象早就创建好了。
-
懒汉式单例:拿对象时,才开始创建对象。
…
应用场景:
- 任务管理器对象、获取运行时对象。
- 在这些业务场景下,使用单例模式,可以避免浪费内存
单例设计模式(懒汉式)⬅️用的不频繁用它
拿对象时,才开始创建对象
写法:
- 把类的构造器私有。
- 定义一个类变量存储类的一个对象。
- 定义一个类方法,保证返回的是同一个对象。
package d6singleInstance;
public class B {
//2.定义一个类变量记住类的一个对象
private static B b;
//1.把类的构造器私有
private B(){
}
//3.定义一个类方法,这个方法要保证第一次调用时才创建一个对象,后面调用时都会用这一个对象返回。
public static B getInstance(){
if (b == null){
System.out.println("第一次创建!");
b = new B();
}
return b;
}
}
package d6singleInstance;
public class Test2 {
public static void main(String[] args) {
B b1 = B.getInstance();
B b2 = B.getInstance();
System.out.println(b1 == b2);
}
}
//
第一次创建!
true
Process finished with exit code 0
🧱面向对象三大特征之二:继承
继承:Java中提供了一个关键字extends,用这个关键字,可以让一个类与另一个类建立起父子关系
public class B extends A{
//A类称为父类(基类或超类)
//B类称为子类(派生类)
}
继承的特点:子类能继承父类的非私有成员(成员变量、成员方法)。
继承后对象的创建:子类的对象是由子类和父类共同完成的
package d7extends;
public class Test {
public static void main(String[] args) {
B b = new B();
System.out.println(b.i);
// System.out.println(b.j); 报错
// System.out.println(b.k); 报错
b.print1();
// b.print2(); 报错
b.print3();
}
}
package d7extends;
//父类
public class A {
//公开成员
public int i;
public void print1(){
System.out.println("===print1===");
}
//私有成员
private int j;
private void print2(){
System.out.println("===print2===");
}
}
package d7extends;
//子类
public class B extends A{
private int k;
//子类可以继承父类的非私有成员
public void print3(){
System.out.println(i);
print1();
// System.out.println(j); 报错
// print2(); 报错
}
}
继承的好处:
减少重复代码的编写
需求:
黑马的员工管理系统中需要处理讲师、咨询师的数据
讲师的数据有:姓名、具备的技能
咨询的数据有:姓名、解答问题的总人数
//➡️Test
package d8extendsapplication;
public class Test {
public static void main(String[] args) {
Teacher t = new Teacher();
t.setName("波仔");
t.setSkill("java Spring");
System.out.println(t.getName());
System.out.println(t.getSkill());
t.printInfo();
}
}
//➡️People
package d8extendsapplication;
public class People {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//➡️Teacher
package d8extendsapplication;
public class Teacher extends People{
private String skill;
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
public void printInfo(){
System.out.println(getName() + "具备的技能有:" + skill);
}
}
//➡️Consultant
package d8extendsapplication;
public class Consultant extends People{
private int num;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
权限修饰符:
就是用来限制类中的成员(成员变量、成员方法、构造器、代码块…)能够被访问的范围
四种:public、 private、 protected、 缺省
//➡️Fu(在本类中)
package d9modifer;
public class Fu {
//1.私有:只能在本类中访问
private void privateMethod(){
System.out.println("==private==");
}
//2.缺省:本类,同一包下的类
void method(){
System.out.println("==缺省==");
}
//3.protected:本类,同一包下的类,任意包下的子类
protected void protectedMethod(){
System.out.println("==protected==");
}
//4.public:本类,同一包下的类,任意包下的子类,任意包下的任一类
public void publicMethod(){
System.out.println("==public==");
}
public void test(){
privateMethod();
method();
protectedMethod();
publicMethod();
}
}
//➡️Demo(同一包下的其它类)
package d9modifer;
public class Demo {
public static void main(String[] args) {
Fu f = new Fu();
// f.privateMethod(); 报错
f.method();
f.protectedMethod();
f.publicMethod();
}
}
//➡️Zi(任意包下的子类)
package d10modifer;
import d9modifer.Fu;
public class Zi extends Fu {
public void test(){
// privateMethod(); 报错
// method(); 报错
protectedMethod();
publicMethod();
}
}
//➡️Demo2(任意包下的任意类)
package d10modifer;
import d9modifer.Fu;
public class Demo2 {
public static void main(String[] args) {
Fu f = new Fu();
// f.privateMethod(); 报错
// f.method(); 报错
// f.protectedMethod(); 报错
f.publicMethod();
Zi zi = new Zi();
// zi.protected(); 子类里能访问,子类外的对象不行
}
}
单继承、Object类
Java是单继承的,Java中的类不支持多继承,但是支持多层继承
object类是Java所有类的祖宗类,我们写的任何一个类都是Object类的子类或子孙类
方法重写
什么是方法重写
当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
重写后,方法的访问,Java会遵循就近原则
package extends_override;
public class Test {
public static void main(String[] args) {
B b = new B();
b.print1();
b.print2(2,3);
}
}
package extends_override;
public class A {
public void print1(){
System.out.println("111");
}
public void print2(int a, int b){
System.out.println("1111111111");
}
}
package extends_override;
public class B extends A{
//方法重写
public void print1(){
System.out.println("666");
}
public void print2(int a, int b){
System.out.println("666666666");
}
}
//响应结果:
666
666666666
Process finished with exit code 0
方法重写的其它注意事项
-
重写小技巧:使用Override注解,他可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。
-
子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限**(public>protected>缺省)**。就是要子类更开放
-
重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
-
私有方法、静态方法不能被重写,如果重写会报错的。
package extends_override;
public class B extends A{
//方法重写 在这里加overvide注解
@Override
public void print1(){
System.out.println("666");
}
@Override
public void print2(int a, int b){
System.out.println("666666666");
}
}
方法重写在开发中的常见应用场景
子类重写Object类的toString()方法,以便返回对象的内容。
package extends_override;
public class Test {
public static void main(String[] args) {
Student s = new Student("lili",19);
System.out.println(s.toString());
}
}
package extends_override;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString(){
return "Student(name=" + name + ",age=" +age;
}
}
//
子类中访问其他成员的特点
1.在子类方法中访问其他成员(成员变量、成员方法),是依照就近原则的
先子类局部范围找。
然后子类成员范围找。
然后父类成员范围找,如果父类范围还没有找到则报错。
//Test
package oop;
public class Test {
public static void main(String[] args) {
S s = new S();
s.showName();
}
}
package oop;
public class F {
String name = "父类名称";
public void print1(){
System.out.println("==父类print1方法执行==");
}
}
package oop;
public class S extends F{
String name = "子类名称";
public void showName(){
String name = "局部名称";
System.out.println(name); //局部名称
System.out.println(this.name); //子类成员变量
System.out.println(super.name); //父类成员变量
}
@Override
public void print1(){
System.out.println("==子类print1方法执行==");
}
public void showMethod(){
print1(); //==子类print1方法执行==
super.print1(); //==父类print1方法执行==
}
}
2、如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要在子类中使用父类的怎么办?
可以通过super关键字,指定访问父类的成员:super.父类成员变量/父类成员方法
子类构造器的特点
-
子类构造器的特点:
子类的全部构造器,都会先调用父类的构造器,再执行自己。
因为继承的时候,在子类的无参构造器里,有一行默认存在super(); -
子类构造器是如何实现调用父类构造器的:
默认情况下,子类全部构造器的第一行代码都是super()(写不写都有),它会调用父类的无参数构造器。
如果父类没有无参数构造器,则我们必须在子类构造器的第一行手写super(…),指定去调用父类的有参数构造器。
常用场景
拼凑好一个对象来源于两个类,且是互相继承的关系
补充知识:this(…)调用兄弟构造器
- 在任意类的构造器中,是可以通过this(…)去调用该类的其他构造器的。
- 用途是:假如说有三个变量,有一个变量每一个对象里都是一样的。就在类里写第三个构造器来给这个变量赋值。这第三个构造器和本来就有的有参构造器和无参构造器为兄弟关系。
- 不能在同一个构造器里又写this和super
- this和super都要在方法中在第一行的位置
总结
🧱面向对象三大特征之三:多态
多态
-
多态是在继承/实现的情况下的一种现象,表现为:对象多态、行为多态(变量不多态,运行谁就是谁)
-
多态的前提:有继承、实现关系;存在父类调用子类;存在方法重写
-
注意事项:多态是对象、行为的多态,java中的属性(成员变量)不谈多态。
//➡️Test
package polymorphism;
public class Test {
public static void main(String[] args) {
People p1 = new Teacher(); //多态的具体代码体现
p1.run(); //识别技巧:编译看左边,运行看右边(写代码的时候看左边的People,跑起来的时候走的是右边的Teacher)
System.out.println(p1.name); //变量识别技巧:编译看左边,运行看左边(因为变量不多态)
People p2 = new Student(); //多态的具体代码体现
p2.run(); //识别技巧:编译看左边,运行看右边
System.out.println(p2.name); //变量识别技巧:编译看左边,运行看左边(因为变量不多态)
}
}
//➡️父类People
package polymorphism;
//父类
public class People {
public String name = "father";
public void run(){
System.out.println("人可以跑");
}
}
//➡️子类
package polymorphism;
public class Teacher extends People{
public String name = "teacher";
@Override
public void run(){
System.out.println("老师跑的气喘吁吁");
}
}
//➡️子类
package polymorphism;
public class Student extends People{
public String name = "Student";
@Override
public void run(){
System.out.println("学生跑得快");
}
}
//
老师跑的气喘吁吁
father
学生跑得快
father
使用多态的好处
-
在多态形式下,右边对象是解耦合的,更便于扩展和维护。
People p1 = new Teacher(); p1.run(); //如果有一天Teacher不好用了,可以把对象随时切换成TeacherNew People p1 = new TeacherNew(); p1.run();
-
定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利。(因为父类范围大)
package polymorphism;
public class Test {
public static void main(String[] args) {
People p1 = new Teacher(); //多态的具体代码体现
p1.run();
//好处2: 可以使用父类类型的变量作为形参,可以接受一切子类对象
Student s = new Student();
go(s);
Teacher t = new Teacher();
go(t);
}
public static void go(People p){
}
}
会产生的一个问题,以及怎么解决?
多态下不能使用子类的独有功能(因为编译的时候过不去)
下一课:类型转换
多态下的类型转换问题
if(p instanceof Student){
Student s = (Student) p;
s.test();
} else{
Teacher t = (Teacher) p;
t.test();
}
final、常量
什么是final
final关键字是最终的意思,可以修饰(类、方法、变量)
修饰类:该类被称为最终类,特点是不能被继承了。
修饰方法:该方法被称为最终方法,特点是不能被重写了
修饰变量:改变量只能被赋值一次
final修饰变量注意事项:
final修饰基本类型的变量,变量存储的数据不能被改变。
final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向的对象的内容可以改变
变量详解
抽象类
什么是抽象类
注意事项
使用抽象类的好处
不是必备语法,但是用了会有好处,会更好的支持多态。
应用场景:模板方法设计模式
建议使用final关键字修饰模板方法
- 模板方法是给对象直接使用的,不能被子类重写
- 一旦子类重写了模板方法,模板方法就失效了。
接口
java提供了一个关键字interface,用这个关键字我们可以定义出一个特殊的结构:接口
public interface 接口名{
//成员变量(常量(所以要大写))
//成员方法(抽象方法(所以不能写方法体))
}
注意:
接口不能创建对象;接口是用来被类实现(implements)的,实现接口的类称为实现类。
修饰符 class 实现类 implements 接口1,接口2,接口3,...{
}
一个类可以实现多个接口(接口可以理解为干爹),实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义为抽象类
接口是用来告诉你要写什么方法,就是定义一种规范
接口的好处:
- 弥补了类单继承的不足,一个类可以同时实现多个接口
- 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。道理同理为多态,改后面的就好。
接口的案例:
接口其他细节(JDK8开始,接口中新增的三种方法)
传统接口我们只能定义抽象方法和常量,但是从JDK8开始,Java允许我们在接口中定义三种新的方法形式
1.默认方法:必须使用default修饰,默认会被public修饰,可以带方法体了
//➡️interface
public interface A {
default void test1(){
System.out.println("===默认方法===");
}
}
//➡️class
public class B implements A {
}
//➡️test
public class Test {
public static void main(String[] args){
B b = new B();
b.test1();
}
}
2.私有方法:必须使用private修饰
//➡️interface
public interface A {
private void test2(){
System.out.println("===私有方法===");
}
}
3.静态方法:必须使用static修饰
static void test3(){
ystem.out.println("===静态方法===");
}
JDK8增强了接口的能力,更便于项目的拓展和维护
接口的多继承
一个接口可以同时继承多个接口
public interface C extends B,A{
}
便于实现类去实现。
1.2.假设一个接口有返回值,一个无返回值,那么不支持多继承,也不支持多实现。