文章目录
- 1. 面向对象编程(基础部分)
- 1.1 入门 hello world
- 1.2 转义字符
- 1.3 数据类型
- 1.4 自动类型转换
- 1.5 运算符
- 1.6 标识符的命名规范
- 1.7 键盘输入语句
- 1.8 流程控制语句
- 1.9 流程控制语句附加
- 1.10 数组 {}
- 1.11 数组的赋值机制
- 1.12 数组的拷贝
- 1.13 数组反转
- 1.14 多维数组 - 二维数组
- 1.15 面向对象
- 1.16 成员方法传参机制(非常重要)
- 1.17 方法递归调用( 非常重要 )
- 1.18 方法重载( OverLoad )
- 1.19 可变参数
- 1.20 作用域
- 1.21 构造方法/构造器
- 1.22 this 关键字
- 1.23 IDEA编程工具相关
- 1.24 包
- 1.25 访问修饰符
- 1.26 面向对象编程有三大特征:封装、继承 和 多态。
1. 面向对象编程(基础部分)
1.1 入门 hello world
public class Main {
public static void main(String[] args) {
System.out.println("hello,world");
}
}
1.2 转义字符
class changeChar {
public static void main(String[] args) {
// \t :一个制表位,实现对齐的功能
// \n :换行符
// \\ :一个\
// \" :一个"
// \' :一个' \r :一个回车
System.out.println("书名\t 作者\t价格\t 销量\n三国\t 罗贯中 \t120\t 1000");
}
}
1.3 数据类型
一、基本数据类型:
- 数值型
1.1 整数类型:存放整数,( byte【1】;short【2】;int【4】;long【8】)
1.2 浮点(小数)类型:( float【4】;double【8】) - 字符型( char【2】),存放当个字符
- 布尔类型( boolean【1】),存放true , false
二、引用数据类型
- 类( class )
- 接口( interface )
- 数组( [] )
class dataType {
public static void main(String[] args) {
char num = 'a' + 1; // a 对应的 UNICODE编码为 97
System.out.println((int) num); // 98
System.out.println(num); // 98 => 对应的字符为 b
}
}
1.4 自动类型转换
- 自动类型转换:当java程序进行赋值或运算时,精度低的类型自动转换为精度高的数据类型
(1) char => int => long => float => double
(2) byte => short => int => long => float => double - 注意事项:
1)有多种类型的数据混合运算时,系统首先自动将所有数据转换成最高的那种数据类型,然后再进行运算
2)当我们把精度(容量)高的数据类型赋值给精度(容量)低的数据类型时,就会报错,反之就会进行自动类型转换
3)byte,short二者与 char 不存在相互自动转换
4)byte,short,char三者可以计算,在计算时首先转换为 int类型
5)boolean不参与转换
6)自动提升原则:表达式结果的类型自动提升为 操作数中最高的数据类型
1.5 运算符
运算符的优先级
1. . () {} ; ,
2. 单目运算符
3. 算数运算符
4. 位移运算符
5. 比较运算符
6. 逻辑运算符
7. 三元运算符
8. 赋值运算符
1.6 标识符的命名规范
- 包名:多单词组成时所有字母都小写:aaa.bbb.ccc
比如 com.hsp.crm - 类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz [大驼峰]
比如: TankShotGame - 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz [小驼峰, 简称 驼峰法]
比如: tankShotGame - 常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
比如 :定义一个所得税率 TAX_RATE
1.7 键盘输入语句
import java.util.Scanner; // 1. 引入相应的java包,表示把 java.util 下的 Scanner类(简单文本扫描器) 导入
public class Lesson85_174 {
public static void main(String[] args) {
// 2.创建 Scanner对象,new 创建一个对象
Scanner myScanner = new Scanner(System.in);
// 3. 接收用户输入了, 使用 相关的方法
System.out.println("请输入名字");
// 当程序执行到 next 方法时,会等待用户输入~~~
String name = myScanner.next(); // 接收用户输入字符串
System.out.println("请输入年龄");
int age = myScanner.nextInt(); // 接收用户输入 int
System.out.println("请输入薪水");
double sal = myScanner.nextDouble(); // 接收用户输入 double
System.out.println("人的信息如下:");
System.out.println("名字=" + name + " 年龄=" + age + " 薪水=" + sal);
}
}
1.8 流程控制语句
在程序中,程序运行的流程控制决定程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句。
- 顺序控制
- 分支控制 2.1 if-else 2.2 switch-case
- 循环控制 3.1 for循环 ; 3.2 while循环 ; 3.3 do…while循环
// 2.1 分支控制 if-else
class ConditionDemo {
public static void main(String[] args) {
Scanner conditionScanner = new Scanner(System.in);
System.out.println("请输入年龄");
int age = conditionScanner.nextInt();
if (age > 18) {
System.out.println("您已成年");
} else if (age == 18) {
System.out.println("您刚满18岁,已成年");
} else {
System.out.println("未成年");
}
}
}
// 2.2 分支控制 switch-case
class ConditionDemo2 {
public static void main(String[] args) {
Scanner monthScanner = new Scanner(System.in);
System.out.println("请输入月份");
int month = monthScanner.nextInt();
switch (month) {
case 3:
case 4:
case 5:
System.out.println("春季");
break;
case 6:
case 7:
case 8:
System.out.println("夏季");
break;
case 9:
case 10:
case 11:
System.out.println("秋季");
break;
case 12:
case 1:
case 2:
System.out.println("冬季");
break;
default:
System.out.println("输入有误");
}
}
}
1.9 流程控制语句附加
1) break 语句用于终止某个语句块的执行,一般使用在 switch 或者循环[for , while , do-while]中
2) continue 语句用于结束本次循环,继续执行下一次循环;
continue 语句出现在多层嵌套的循环语句体中时,可以通过标签指明要跳过的是哪一层循环 , 这个和前面的标签的使用的规则一样。
3)return 使用在方法,表示跳出所在的方法
1.10 数组 {}
动态初始化:
数据类型 数组名[] = new 数据类型[大小]; ( 写法2:数据类型[] 数组名 = new 数据类型[大小]; )
静态初始化:
数据类型 数组名[] = { 元素1,元素2,… }
注意事项:
- 数组是多个相同类型数据的组合,实现对这些数据的统一管理; ( 同样遵守自动类型转换规则 )
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用;
- 数组创建后,如果没有赋值,有默认值
int 0, short 0, byte 0, long 0, float 0.0, double 0.0, char \u0000, boolean false, String null 。
class ArrayDemo {
public static void main(String[] args) {
// double arrayList[] = new double[5]; // 动态初始化
double arrayList[] = {1.0, 2.0, 3.0, 4.0, 27}; // 静态初始化
int i = 0;
for (; i < 5; i++) {
System.out.println("数组的第" + i + "项" + arrayList[i]);
// if (i == 3) {
// return; // break; / continue;
// }
// System.out.println("test~~");
}
System.out.println("go on...");
}
}
1.11 数组的赋值机制
- 基本数据类型赋值,这个值就是具体的数据,而且相互不影响。
int n1 = 2; int n2 = n1; - 数组在默认情况下是引用传递,赋的值是地址,会相互影响。( 堆栈存储方式:栈中存放地址,值存放在堆中。 )
int[] arr1 = {1,2,3}; int[] arr2 = arr1;
1.12 数组的拷贝
// 正确数组拷贝方式是通过数组遍历的方式来实现的。
class ArrAyCopy {
public static void main(String[] args) {
int arrayList[] = {1, 2, 3};
int arrayList2[] = new int[arrayList.length];
// 要求:拷贝一份 arrayList 给 arrayList2,二者数据不会相互影响
// arrayList2 = arrayList; // 错误方式,直接赋值,赋值的是栈中的地址,指向共同的存放数据的堆
for (int i = 0; i < arrayList.length; i++) {
arrayList2[i] = arrayList[i];
}
for (int i = 0; i < arrayList.length; i++) {
System.out.println("arrayList:第" + i + "项" + arrayList[i]);
System.out.println("arrayList2:第" + i + "项" + arrayList2[i]);
}
System.out.println("--------------------------");
arrayList2[1] = 20;
for (int i = 0; i < arrayList.length; i++) {
System.out.println("arrayList:第" + i + "项" + arrayList[i]);
System.out.println("arrayList2:第" + i + "项" + arrayList2[i]);
}
}
}
1.13 数组反转
数组延伸
- 数组扩容 ;
- 数组缩减( 思路类似于 方法2 )
- 数组排序
3.1 冒泡排序 - 数组查找
4.1 顺序查找
4.2 二分查找【二分法】
class ArrayReverse {
public static void main(String[] args) {
int arrayList[] = {1, 2, 3, 4, 5, 6};
// 方法1:
// for(int i=0;i<arrayList.length/2;i++){
// int saveItem = arrayList[arrayList.length-1-i];
// arrayList[arrayList.length-1-i] = arrayList[i];
// arrayList[i] = saveItem;
// }
// 方法2:
int arrayList2[] = new int[arrayList.length];
for (int i = arrayList.length - 1, j = 0; i >= 0; i--, j++) {
arrayList2[j] = arrayList[i];
}
for (int i = 0; i < arrayList.length; i++) {
// System.out.println(arrayList[i]);
System.out.println(arrayList2[i]);
}
}
}
1.14 多维数组 - 二维数组
动态初始化:( 注:列数不确定时,new 数据类型[大小][] )
写法1:数据类型 数组名[][] = new 数据类型[大小][大小];
写法2:数据类型[][] 数组名 = new 数据类型[大小][大小];
写法3:数据类型[] 数组名[] = new 数据类型[大小][大小];
例:int[] x,y[]; // x是int类型的一维数组,y是int类型的二维数组
静态初始化:
数据类型 数组名[][] = {{ 元素1,元素2,… }, { 元素1,元素2,… }, { 元素1,元素2,… }}
public class Lesson175_226 {
public static void main(String[] args) {
// 需求:动态创建下面二维数组,并输出
// i = 0: 1
// i = 1: 2 2
// i = 2: 3 3 3
int arrList[][] = new int[3][]; // new int[3][] ,列数不确定
for (int i = 0; i < arrList.length; i++) {
arrList[i] = new int[i + 1];
for (int j = 0; j < arrList[i].length; j++) {
arrList[i][j] = i + 1;
}
}
for (int i = 0; i < arrList.length; i++) {
for (int j = 0; j < arrList[i].length; j++) {
System.out.print(arrList[i][j] + "\t");
}
System.out.print("\n");
}
}
}
1.15 面向对象
- 类和对象
1.1 语法定义:
1) 声明类:
class Person {
// 属性;成员变量;field字段;
数据类型 属性名称;
};
2)创建对象:
Person p1 = new Person();
1.2 注意事项:
- 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
这里简单的介绍访问修饰符: 控制属性的访问范围
有四种访问修饰符 public, proctected, 默认, private ,后面我会详细介绍 - 属性的定义类型可以为任意类型,包含基本类型或引用类型
- 属性如果不赋值,有默认值,规则和数组一致。具体说:
int 0,short 0, byte 0 , long 0 , float 0.0, double 0.0,char \u0000,boolean false,String null
- 成员方法( 方法 )
2.1 语法定义:
访问修饰符 返回数据类型 方法名(形参列表..) {//方法体
语句;
return 返回值;
}
2.2 注意事项:
√ 访问修饰符 (作用是控制 方法使用的范围)
如果不写默认访问,[有四种: public, protected, 默认, private], 具体在后面说
√ 返回数据类型
- 一个方法最多有一个返回值 [思考,如何返回多个结果 返回数组 ]
- 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
- 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值; 而且要求返回值类型必须和 return 的
值类型一致或兼容 - 如果方法是 void,则方法体中可以没有 return 语句,或者 只写 return ;
√ 方法名
遵循驼峰命名法,最好见名知义,表达出该功能的意思即可, 比如 得到两个数的和 getSum, 开发中按照规范
√ 形参列表
1)一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开
2)参数类型可以为任意类型,包含基本类型或者引用数据类型
3)调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数(兼容:就是遵循自动类型转换规则)
4)方法定义时的参数称为形式参数,简称形参;方法调用时传入的参数称为实际参数,简称实参;实参和形参的类型要一致或兼容、个数、顺序也必须一致!
√ 形参列表
里面写 完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!即:方法不能嵌套定义。
√ 方法调用细节说明(!!!)
1)同一个类中的方法调用:直接调用即可。
2)跨类中的方法 A类 调用 B类方法:需要通过对象名调用。
3)跨类的方法调用和方法的访问修饰符相关。
class ObjectFace {
public static void main(String[] args) {
// 创建 Information 对象
// person01 是对象名(对象引用)
// new Information() 创建的对象空间(数据) 才是真正的对象
Information person01 = new Information();
person01.name = "张三";
person01.sex = '0';
person01.age = 18;
person01.weight = 100;
System.out.println("person01 姓名:" + person01.name + " " + "性别:" + person01.sex + " " + "年龄:" + person01.age + " " + "体重:" + person01.weight);
Information person02 = person01;
System.out.println("person02 姓名:" + person02.name);
person01.speaking();
// 这里调用传的参数称为 实参
int sum = person01.getSum(20, 7);
System.out.println("求两个数的和为:" + sum);
}
}
class Information {
// 190.1 类和对象
// 属性;成员变量;field字段;
String name;
byte sex;
int age;
double weight;
// 190.2 成员方法( 方法 )
// 解读:
//1. public 表示方法是公开
//2. void : 表示方法没有返回值
//3. speak() : speak 是方法名, () 形参列表
//4. {} 方法体,可以写我们要执行的代码
//5. System.out.println("我是一个好人"); 表示我们的方法就是输出一句话
public void speaking() {
System.out.println("成员方法 speaking()");
}
// 解读:
//1. public 表示方法是公开的
//2. int :表示方法执行后,返回一个 int 值
//3. getSum 方法名
//4. (int num1, int num2) 形参列表,2 个形参,可以接收用户传入的两个数
//5. return res; 表示把 res 的值, 返回
public int getSum(int num1, int num2) {
return num1 + num2;
}
}
1.16 成员方法传参机制(非常重要)
- 基本数据类型的传参机制
基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参! - 引用数据类型的传参机制
引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参!(如果要编写一个方法复制一个对象,使得这个复制的对象与原对象不相互影响,可以在方法体, 创建一个新对象,并复制属性,返回即可)
class MethodExercise02 {
//编写一个 main 方法
public static void main(String[] args) {
//编写一个方法 copyPerson,可以复制一个 Person 对象,返回复制的对象。克隆对象,
//注意要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同
Person p = new Person();
p.name = "张三";
p.age = 10;
MyTools tools = new MyTools();
Person p2 = tools.copyPerson(p);
p2.name = "李四";
p2.age = 20;
System.out.println("p 的属性 名字=" + p.name + " age=" + p.age);
System.out.println("p2 的属性 名字=" + p2.name + " age=" + p2.age);
//对象比较看看是否为同一个对象
System.out.println(p == p2);//false
}
}
class Person {
String name;
int age;
}
class MyTools {
//编写方法的思路
//1. 方法的返回类型 Person
//2. 方法的名字 copyPerson
//3. 方法的形参 (Person p)
//4. 方法体, 创建一个新对象,并复制属性,返回即可
public Person copyPerson(Person p) {
//创建一个新的对象
Person p2 = new Person();
p2.name = p.name;
p2.age = p.age;
return p2;
}
}
1.17 方法递归调用( 非常重要 )
递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂问题,同时可以让代码变得简洁
递归的重要规则
1)执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2)方法的局部变量是独立的,不会相互影响
3)如果方法中使用的是引用类型变量(比如数组、对象),就会共享该引用类型的数据
4)递归必须向退出递归的条件逼近,否则就是无限递归,出现 stackOverflowError,死龟( 栈溢出 )
5)当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕
class Recursion01 {
//编写一个 main 方法
public static void main(String[] args) {
T t1 = new T();
// 案例1:输出什么?
t1.test(4); // n=2 n=3 n=4
// 案例2:阶乘
int res = t1.factorial(5);
System.out.println("5 的阶乘 res =" + res); // 120
// n= 5 4 3 2 1
// 执行顺序按箭头方向执行
// === 递归调用带入 n 的值 得到如下所示 === >
// return= factorial(4) * 5 factorial(3) * 4 factorial(4) * 3 factorial(4) * 2 1
// < === 然后再通过每次的return 累计想乘 ===
// res= 1 2 6 24 120
}
}
class T {
public void test(int n) {
if (n > 2) {
test(n - 1);
}
System.out.println("n=" + n);
}
//factorial 阶乘
public int factorial(int n) {
if (n == 1) {
return 1;
} else {
return factorial(n - 1) * n;
}
}
}
1.18 方法重载( OverLoad )
java 中允许同一个类中,多个同名方法的存在,但要求 形参列表不一致!
√ 注意事项( 触发方法重载的必要条件 )
1)方法名:必须相同
2)形参列表:形参类型 或 个数 或 顺序,至少有一样不同,形参名称无要求
3)返回类型:无要求
√ 重载的好处:
- 减轻了起名的麻烦
- 减轻了记名的麻烦
public class Lesson227_262 {
public static void main(String[] args) {
sumClass result = new sumClass();
// 方法重新,会根据传入的参数 自动匹配到对应的 sum方法
System.out.println("result=" + " " + result.sum(1, 1));
System.out.println("result=" + " " + result.sum(1.1, 1));
System.out.println("result=" + " " + result.sum(1.1, 1));
System.out.println("result=" + " " + result.sum(1.1, 1, 3));
}
}
class sumClass {
public int sum(int a, int b) {
System.out.println("111");
return a + b;
}
;
public double sum(double a, int b) {
System.out.println("222");
return a + b;
}
;
public double sum(double a, int b, int c) {
System.out.println("333");
return a + b + c;
}
;
}
1.19 可变参数
java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。就可以通过可变参数实现
√ 基本语法
访问修饰符 返回类型 方法名(数据类型… 形参名) {}
√ 注意事项
1)可变参数的实参可以为 0个或任意多个 ( 数据类型… 形参名 被称为可变参数 )
2)可变参数的实参可以为数组
3)可变参数的本质就是数组
4)可变参数可以和普通参数一起放在形参列表,但必须保证可变参数在最后
5)一个形参列表中只能出现一个可变参数
class changeParams {
public static void main(String[] args) {
betterSumClass betterSum = new betterSumClass();
System.out.println("sum=" + betterSum.sum(1, 2, 3));
System.out.println("sum=" + betterSum.sum(1.1, 2, 3.3, 4, 5.5));
}
}
class betterSumClass {
// 上面 sumClass类 中的 sum方法重载 代码太过冗余,优化如下:
public double sum(double... params) {
double result = 0;
for (int i = 0; i < params.length; i++) {
result += params[i];
}
return result;
}
}
1.20 作用域
√ 基本使用
1)在java编程中主要的变量就是 属性(成员变量) 和 局部变量
2)局部变量一般指在成员方法中定义的变量。
3)java中作用域的分类:
3.1 全局变量:也就是属性,作用域为整个类体
3.2 局部变量:也就是除了属性之外的其他变量,作用域为定义他的代码块中
4)全局变量(属性)可以不赋值,直接使用,因为会有默认值;局部变量必须赋值后才能使用,因为没有默认值
√ 注意事项
1)属性和局部变量可以重名,访问时遵循就近原则
2)在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名
3)属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁;
局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁,即在一次方法调用过程中
4)作用域范围不同
全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
局部变量:只能在本类中对应的方法中使用
5)修饰符不同
全局变量/属性 可以加 修饰符(public protected private…)
局部变量 不可以加 修饰符
class VarScopeDetail {
public static void main(String[] args) {
Per p1 = new Per();
/*
属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。
局部变量,生命周期较短,伴随着它的代码块的执行而创建,
伴随着代码块的结束而销毁。即在一次方法调用过程中
*/
//p1.say();//当执行 say 方法时,say 方法的局部变量比如 name,会创建,当 say 执行完毕后
//name 局部变量就销毁,但是属性(全局变量)仍然可以使用
Ts t1 = new Ts();
//全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
t1.test(); //第 1 种跨类访问对象属性的方式
t1.test2(p1);//第 2 种跨类访问对象属性的方式
}
}
class Ts {
public void test() {
Per p1 = new Per();
System.out.println(p1.name);//jack
}
//
public void test2(Per p) {
System.out.println(p.name);//jack
}
}
class Per {
//细节: 属性可以加修饰符(public protected private..)
public int age = 20;
String name = "jack";
public void say() {
// 局部变量不能加修饰符
// public int age = 20;
//细节 属性和局部变量可以重名,访问时遵循就近原则
String name = "king";
System.out.println("say() name=" + name);
}
public void hi() {
String address = "北京";
//String address = "上海";//错误,重复定义变量
String name = "hsp";//可以
}
}
1.21 构造方法/构造器
构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:
基本语法
[修饰符] 方法名(形参列表){
方法体;
}
说明:
- 构造器的修饰符可以默认, 也可以是 public protected private
- 构造器没有返回值
- 构造方法名 和类名字 必须一样
- 参数列表 和 成员方法 一样的规则
- 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。( 构造器的调用, 由系统完成 )
√ 注意事项
- 一个类可以定义多个不同的构造器,即构造器重载
- 构造器是完成对象的初始化,并不是创建对象
- 如果程序没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器,比如: Cat(){} )
- 一旦定义了自己的构造器,默认的构造器就被覆盖了,就不能再使用默认的无参构造器,除非显式的再定义一下,即 Cat(){}()
class Constructor {
public static void main(String[] args) {
// /当 new 一个对象时,直接通过构造器设置 名字和年龄 初始值
Cat c = new Cat("Tom", 18);
System.out.println("名字=" + c.name + " " + "年龄=" + c.age); // 名字=Tom 年龄=0
}
}
class Cat {
String name;
int age;
// 系统自动生成的 默认构造器,但是在下面 Cat(String reName, int reAge){ ... } 定义后,此默认器会被覆盖,要想继续使用需再次定义
// Cat(){
// }
// 构造方法/构造器
public Cat(String reName, int reAge) {
System.out.println("构造方法触发~~");
name = reName;
reAge = reAge;
}
}
1.22 this 关键字
哪个对象调用,this就代表哪个对象
√ 注意事项:
- this 关键字可以用来访问本类的属性、方法、构造器
- this 用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表);
- 访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一
条语句) - this 不能在类定义的外部使用,只能在类定义的方法中使用。
class thisKey {
public static void main(String[] args) {
dog t1 = new dog("Bob", 8);
// 没使用 this时,此处打印的 name=null age=0
// 使用 this 后
System.out.println("name=" + t1.name + " " + "age=" + t1.age); // name=Bob age=8
dog t2 = new dog("Buluto", 3);
// System.out.println("name=" + t2.name + " " + "age=" + t2.age); // name=Buluto age=3
}
}
class dog {
String name;
int age;
public dog(String name, int age) {
// name = name;
// age = age;
// 在没有改变 对象的属性值时,都为默认值或初始值
System.out.println("this.name=" + this.name + " " + "this.age=" + this.age); // this.name=null this.age=0
System.out.println("name=" + name + " " + "age=" + age); // name=Bob age=8
this.name = name;
this.age = age;
}
}
1.23 IDEA编程工具相关
快捷键使用:
1)自动的分配变量名 , 通过 在后面假 .var
2)自动生成构造器、设置/获取属性值 等 alt + insert
3)补全代码 alt + /
4)导入该行需要的类 先配置 auto import , 然后使用 alt+enter 即可
5)查看一个类的层级关系 ctrl + H [学习继承后,非常有用]
6)将光标放在一个方法上,输入 ctrl + B , 可以定位到方法 [学继承后,非常有用]
代码模版使用:
1)main ( 快速生成 main方法 )
2)fori ( 快速生成for循环 )
3)sout ( 快速生成打印语句 )
如果自己想添加其他代码模版:file -> setting -> editor -> Live templates
1.24 包
应用场景:在一个java项目中,想同时定义一个 相同名称的类时,可以使用包。
√ 包的本质(原理):包的本质实际上就是创建不同的文件夹/目录来保存类文件。
√ 包的基本语法
package com.studyPkg.demo
说明:1. package 关键字,表示打包 2. com.studyPkg.demo 表示包名
√ 包的命名
命名规则:只能包含数字、字母、下划线、小圆点(.),但是不能以数字开头,不能是关键字或保留字
命名规范:项目中一般是 小写字母 + 小圆点,com.公司名.项目名.业务模块名
比如: com.sina.crm.user; // 用户模块
√ 包的引入( 一般小圆点可以视为 文件路径中的 \ )
语法: import 包;
目的:我们引入一个包就是为了使用该包下的 类
例如:import java.util.Scanner; //引入一个 Scanner类
√ 注意事项
1)package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一句 package
2)import 需放在 package 下面,在类定义前面,可以有多句 且 没有顺序要求
3)需要使用到哪个类,就导入哪个类即可,不建议使用 *导入
√ 常用的包
一个包下,包含很多的类,java 中常用的包有:
- java.lang.* // lang 包是基本包,默认引入,不需要再引入.
- java.util.* // util 包,系统提供的工具包, 工具类,使用 Scanner
- java.net.* // 网络包,网络开发
- java.awt.* // 是做 java 的界面开发,GUI
√ 包的三大作用:
1)区分相同名称的类
2)当类很多时,可以很好的管理类
3)控制访问范围
案例: com.studyPackage272.demo01.pkgUse 【 package:包 】
1.25 访问修饰符
√ 基本介绍
java 提供四种访问控制修饰符号,用于控制 方法和属性(成员变量) 的访问权限(范围):
- 公开级别:用 public 修饰,对外公开
- 受保护级别:用 protected 修饰,对子类和同一个包中的类公开
- 默认级别:没有修饰符号,向同一个包的类公开.
- 私有级别:用 private 修饰,只有类本身可以访问,不对外公开.
√ 4种访问修饰符的访问范围
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|---|
公开 | public | √ | √ | √ | √ |
受保护 | protected | √ | √ | √ | × |
默认 | 无修饰符 | √ | √ | × | × |
私有 | private | √ | × | × | × |
√ 注意事项
1)修饰符可以用来修饰类中的属性、成员方法 以及 类
2)只有 默认的 和 public 才能修饰类,并且遵循上述访问权限的特点
3)子类中的访问权限放在继承中讲解
4)成员方法的访问规则和属性完全一样
// 案例: com.studyModifier278.demo01.modifier01 【 modifier:修饰符 】
1.26 面向对象编程有三大特征:封装、继承 和 多态。
1.26.1面向对象编程三大特征 - 封装
√ 封装
封装就是把抽象出的数据【属性】和对数据的操作【方法】封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作【方法】,才能对数据进行操作
√ 封装的实现步骤( 三步 )
1)将 属性 进行私有化 private 【 不能直接修改属性 】
2)提供一个公共的( public ) set方法,用于对属性判断并赋值
public void setXxx(){
// 加入数据验证的业务逻辑
属性 = 参数名;
}
3)提供一个公共的( public )get方法,用于获取属性的值
public void getXxx(){
// 加入权限判断的业务逻辑
属性 = 参数名;
}
√ 封装的好处
1)隐藏实现细节:方法 <-- 调用
2)可以对数据进行验证,保证安全合理
案例:com.studyEncapsulation.demo01.Account 【 encapsulation:封装 】
1.26.2 面向对象编程三大特征 - 继承
√ 继承
继承可以实现代码的复用。当多个类存在相同的属性(成员变量)和 方法时,可以从这些类中 抽象 出父类,父类中定义这些类中 相同的属性和方法,所有的子类
不需要重新定义这些属性和方法,只需要通过 extends来声明继承父类即可。
√ 基本语法
class 子类 extends 父类{
}
附加说明:
1)子类就会自动拥有父类定义的属性和方法
2)父类又叫 超类、基类
3)子类又叫派生类
√ 注意事项
- 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访
问,要通过父类提供公共的方法去访问 - 子类必须调用父类的构造器, 完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,
则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过 - 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
- super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
- super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java 所有类都是 Object 类的子类, Object 是所有类的基类. 8) 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
- 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
例如:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】 - 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
5继承的好处
- 代码的复用性提高了
- 代码的扩展性和维护性提高了
// 案例:com.studyExtends.demo01.Human 【 extends:继承 】
1.26.3 super 关键字
√ 基本介绍
super代表父类的引用,用于访问父类的属性、方法、构造器
√ 基本语法
- 访问父类的属性,但不能访问父类的 private属性
super.属性名; - 访问父类的方法,但不能访问父类的 private方法
super.方法名(参数列表); - 访问父类的构造器,只能放在构造器的第一句,只能出现一句
super(参数列表);
√ super的好处
- 调用父类的构造器的好处( 分工明确,父类属性由父类初始化,子类的属性由子类初始化 )
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this,直接访问是一样的效果
- super的访问不限于直接父类,如果爷爷类和本类中都有同名的成员,使用super访问遵循就近原则。Son->Father->Grandpa,当然也需要遵守访问
权限的相关规则
√ super的好处
区别点 | this | super |
---|---|---|
访问属性 | 访问本类中的属性,如果本类没有此属性,则从父类中继续查找。 | 从父类开始查找属性。 |
调用方法 | 访问本类中的方法,如果本类没有此方法,则从父类继续查找。 | 从父类开始查找方法。 |
调用构造器 | 调用本类构造器,必须放在构造器的首行。 | 调用父类构造器,必须放在子类构造器的首行。 |
特殊 | 表示当前对象。 | 子类中访问父类对象。 |
// 案例:com.studySuper297.demo01.Son
1.26.4 方法重写/覆盖
√ 基本介绍
简言而之,方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法。
√ 注意事项( 触发 方法重写/覆盖 的条件 )
- 子类的方法的 形参列表、方法名称 ,要和父类方法的 形参列表、方法名称 完全一样。
- 子类方法的返回类型和父类方法返回类型一样,或者是 父类返回类型的子类
- 子类方法不能缩小父类方法的访问权限。 public -> protected -> 默认 -> private
√ 方法重载 与 重写 对比
名称 发生范围 方法名 形参列表 返回类型 修饰符
重载(overload) 本类 必须一样 类型,个数或者顺序 无要求 无要求
至少有一个不同
重写( override ) 父子类 必须一样 相同 子类重写的方法,返回 子类方法不能缩小父类
的类型和父类返回的类 方法的访问范围
型一致,或者是其子类
// 案例:com.study06Override301.demo01.TestDemo 【override:覆盖】
1.26.5 面向对象编程三大特征 - 多态
√ 基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
√ 对象的多态( 重要 )
- 一个对象的 编译类型 和 运行类型 可以不一致
- 编译类型在定义对象的时候就确定了,不能改变
- 运行类型是 可以改变的
- 编译类型看定义时 = 的 左边,运行类型看 = 的 右边
例如:
Animal animal = new Animal(“猫咪01”, 1); // animal 的编译类型是Animal,运行类型是 Dog
animal = new Dog(“狗子”, 2); // animal 的编译类型仍然是 Animal,运行类型 变成了 Dog
√ 注意事项
多态的前提是: 两个对象(类) 之间存在 继承的关系
多态的 向上转型:
1)本质:父类的引用指向了 子类的对象
2)语法:父类类型 引用名 = new 子类类型();
3)特点:
编译类型看左边,运行类型看右边;
可以调用父类中所有成员( 需遵守访问权限 )
不能调用子类中特有成员
最终运行效果看子类的具体实现
多态的 向下转型
1)语法:子类类型 引用名 = (子类类型)父类引用;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,可以调用子类类型中所有的成员
√ java的动态绑定机制( 重要 )
1)当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
2)当调用对象属性时,没有动态绑定机制,哪里声明,哪里调用
√ instanceOf 比较操作符,用于判断对象的运行类型是否为 XX类型 或 XX类型的子类型
// 案例:com.study07Polymorphic305.demo01.TestDemo01 【 polymorphic:多态 】