三. 面向对象
-
IDEA的使用:
-
新建工程:New Project -> Empty Project(最后一项),IDEA中project相当于eclipse中workspace
-
自动弹出Project structure窗口,先取消掉
-
在空的工程下新建Module(模块):File -> new -> Moudle,IDEA中模块类似于eclipse中的project
-
选择Java,点击next,给module命名
-
在src上右键 -> New -> Java Class,创建类
IDEA字体设置:File -> Settings -> 输入font搜索,设置字体大小及样式 ->Apply -> Ok
快速生成main方法:psvm + 回车
快速生成System.out.println();:sout + 回车
IDEA是自动保存,不需要Ctrl + s
删除一行:Ctrl + y
运行:代码上右键run或者点击绿色三角,或Ctrl + Shift + F10
左侧目录栏键盘控制:左箭头收起,右箭头展开,上下键移动
IDEA中退出任何窗口都可以用Esc键
新增/新建/添加的快捷键:Alt + insert,然后输入来查找
窗口变大/变小:Ctrl + Shift + F12
java代码界面间切换:Alt + 左箭头/右箭头
切换窗口:Alt + 标号
提示方法参数:Ctrl + p
查看访问的属性/方法:按住Ctrl,点击左键;查看所有属性/方法:Ctrl + o
单行注释:Ctrl + / 多行注释:Ctrl + Shift + /
纠错快捷键:alt + 回车
多行注释:Ctrl + shift + /
查看类的属性、方法:Ctrl + F12 -
-
面向对象特征:封装
-
封装的好处:
- 封装之后,对事物来说看不到复杂的一面,只能看到该事物简单的一面。复杂性封装,对外提供简单的操作入口
- 封装之后才会形成真正的“对象”、真正的“独立体”
- 封装意味着程序可以重复使用,且适用性更强
- 封装后提高了事物的安全性
public class User{ //没有进行封装
int age; //User类中的age属性在外部程序中可以随意访问,导致age属性不安全
}
public class UserTest{
public static void main(String[] args){
User user = new User();
System.out.println(user.age); //输出0
user.age = -100;
System.out.println(user.age);
//输出-100,age不可能为负,而在运行时并未报错
}
}
-
封装的步骤:
-
所有属性私有化,使用private关键字修饰,private表示私有,修饰的所有数据只能在本类中访问
-
对外提供简单的操作入口,提供两个公开的方法,分别是set方法和get方法
读取数据:调用get方法
修改属性:调用set方法
-
set方法命名规范:(接上例)
public void setAge (int a){
age = a;
} -
get方法命名规范:
public int getAge (){
return age;
} -
setter和getter方法没有static关键字
调用有static关键字修饰的方法:类名.方法名(实参);
没有static关键字修饰的方法调用方法:引用.方法名(实参);
-
public class User{
private int age;
}
public calss UsterTest{
User user = new User();
System.out.println(user.age); //编译报错,age属性私有化,外部程序不能直接访问
}
public class User{
private int age;
public void setAge(int age){
age = age; //编译报错
}
public void setAge(int a){ //setter
age = a;
}
public int getAge(){ //getter
return age;
}
}
public class UserTest{
public static void main(String[] args){
User user = new User();
user.setAge(-100); //修改
System.out.println(user.getAge()); //读取,依然可以是负数
}
}
//修改setAge方法:
public void setAge(int a){
if(a < 0 || a > 150){ //加入判断
System.our.println("年龄不合法");
return;
}else{
age = a;
}
}
-
关于Java中的构造方法:
-
构造方法又被称为构造函数/构造器/Constructor
-
构造方法语法结构:
[修饰符列表] 构造方法名 (形式参数列表) {
构造方法体;
} -
普通方法语法结构:
[修饰符列表] 返回值类型 方法名 (形式参数列表) {
方法体;
} -
对于构造方法来说,“返回值类型”不需要指定,也不能写void(带返回值类型的是普通方法)
-
构造方法的方法名必须和类名保持一致
-
构造方法的作用:通过构造方法的调用,可以创建对象(同时初始化实例变量的内存空间)
-
构造方法的调用:
-
普通方法调用:(有static修饰符)类名.方法名(实参列表);
没有static修饰符:引用.方法名(实参列表); -
构造方法调用:new 构造方法名(实参列表);
-
-
每一个构造方法执行结束后都有返回值(返回值类型为构造方法所在类的类型),但是“return 值;”语句不用写
-
当一个类中没有任何方法时,系统会默认给该类提供一个无参数的构造方法,该构造方法称为缺省构造器
-
当一个类定义了构造方法,系统不再提供缺省构造器(建议为当前类提供无参数构造方法,无参数构造方法常用)
-
public class User{
}
public class UserTest{
public static void main(String[] args){
new User(); //编译通过
}
}
public class User{
public User(){
System.out.println("abc");
}
}
public class UserTest{
public static void main(String[] args){
new User(); //输出abc
}
}
public class User{
public User(){ //无参数构造方法
System.out.println("User's Default ConsTructor Invoke!")
}
//有参数的构造方法
public User(int i){
System.out.println("带有int类型参数的构造器")
}
public User(String name){
System.out.println("带有String类型参数的构造器")
}
public User(int i,String name){
System.out.println("带有int,String类型的构造器")
}
}
public class UserTest{
public static void main(String[] args){
User u1 = new User(); //以下程序创建了4个对象,在“堆内存”中开辟空间
User u2 = new User(10);
User u3 = new User("abc");
User u4 = new User(10,"abc");
}
}
public class Account {
private String actno;
private double balance;
public Account(){} //无参数构造方法
public Account(String a){
actno = a;
}
public Account(double b){
balance = b;
}
public Account(String c,double d){
actno = c;
balance = d;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
public class UserTest {
public static void main(String[] args){
Account a1 = new Account();
System.out.println(a1.getActno()); //null
System.out.println(a1.getBalance()); //0.0
Account a2 = new Account("abc");
System.out.println(a2.getActno()); //abc
System.out.println(a2.getBalance()); //0.0
Account a3 = new Account(100.0);
System.out.println(a3.getActno()); //null
System.out.println(a3.getBalance()); //100.0
Account a4 = new Account("abc",100.0);
System.out.println(a4.getActno()); //abc
System.out.println(a4.getBalance()); //100.0
}
}
public class Test01 {
public static void add(int i){
i ++;
System.out.println(i); //输出11
}
public static void main(String[] args) {
int i = 10;
add(i); //等同于add(10);
System.out.println(i); //输出10
}
}
参数传递时,java只遵循一种语法机制:传递变量中保存的值,只是有时这个值是字面值,有时这个值是另一个java对象的内存地址
public class Test01 {
public static void main(String[] args) {
User u = new User(20); //User u = 0x1234
add(u); //等同于add(0x1234),传递u的值(u保存的是对象的地址)
System.out.println(u.age); //21
}
static void add(User u) {
u.age++;
System.out.println(u.age); //21
}
}
class User {
int age; //实例变量
public User(int i) { //构造方法
age = i;
}
}
-
this关键字
- this是一个关键字,意为:这个
- this是一个引用/变量,this变量中保存了内存地址指向自身,this存储在JVM堆内存java对象内部
- 每创建一个对象就有一个this
- this可以出现在“实例方法”中,指向当前执行动作的对象
- this不能使用在带有static关键字的方法中
-
没有static关键字的方法称为“实例方法”,使用“引用.”访问
没有static关键字的方法称为“实例变量”
-
当一个行为/动作执行过程中需要对象参与时,该方法要定义为“实例方法”,不能带static
public class Customer {
String name;
public Customer(){ //顾客购物的行为,不带static关键字的方法
//由于每一个对象在执行购物动作时结果不同,所以购物动作必须有“对象”的参与
}
public void shopping(){
System.out.println(this.name + "在购物");
//由于name是一个实例变量,所以name访问时一定是当前对象的name
//所以多数情况下“this.”可以省略:System.out.println(name + "在购物");
}
public static void doSome(){
System.out.println(this.name); //编译错误,执行过程中没有“当前对象”
//无法从静态上下文中引用非静态 变量 name
//与之前学的“变量作用域”内容有不同
}
}
public class CustomerTest {
public static void main(String[] args) {
//在对象内部生成this变量,存储内容与c1相同,都指向对象
Customer c1 = new Customer();
c1.name = "zhangsan";
Customer c2 = new Customer();
//使用c2访问对象时,过程中出现的this就是c2
c2.name = "lisi";
System.out.println(c1.name); //输出zhangsan
System.out.println(c2.name); //输出lisi
c1.shopping(); //调用shopping方法,输出zhangsan在购物
c2.shopping(); //输出lisi在购物
}
}
public class abc {
int i = 10; //实例
public static void main(String[] args) {
abc.doSome(); //编译错误,带有static的方法中不能“直接”访问实例变量和实例方法
System.out,println(i); //编译错误,必须用“引用.”的方式访问
abc a = new abc();
a.run();
}
public void doSome(){
System.out.println("do some");
}
public void run(){
System.out.println("run");
doSome(); //编译通过,意为调用当前对象的doSome方法
//run是实例方法,调用时一定存在对象,此处省略了“this.”
}
}
- 用来区分局部变量和实例变量时,“this.”不能省略
public class User {
private int id;
public void setId(int id) {
//this不能省略
this.id = id; //this.id是实例变量,等号后边的id是局部变量id
}
public User(int id,String name){
this.id = id; //this不能省略
this.name = name;
}
}
-
用于构造方法:通过当前构造方法调用其他构造方法
语法格式:this(实参);
this()语法只能出现在构造函数第一行(一个构造方法中只能出现一次)
public class Date {
private int year;
private int month;
private int day;
public Date(){ //需求:调用无参数构造方法时,默认日期为“1970-1-1”
this(1970,1,1); //不创建对象,又能调用其它构造方法
}
public Date(int year,int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
public static void main(String[] args) {
Date d1 = new Date();
Date d2 = new Date(1998,1,22);
System.out.println(d1.year + "年" + d1.month + "月" + d1.day + "日");
System.out.println(d2.year + "年" + d2.month + "月" + d2.day + "日");
}
//setter和getter方法省略
}
- 练习题
public class test01 {
int i = 10;
public static void doSome(){
System.out.println("do some");
}
public void doOther(){
System.out.println("do other");
}
public static void method1(){
test01.doSome(); //完整调用static方法
doSome(); //省略调用static方法
test01 doother = new test01();
doother.doOther(); //编译成功
this.doOther(); //编译报错:无法从静态上下文中引用非静态变量
System.out.println(doother.i);
}
public void method2(){
test01.doSome();
doSome();
test01 other = new test01();
other.doOther();
doOther(); //编译通过
System.out.println(other.i);
System.out.println(i);
}
public static void main(String[] args) {
test01.method1();
method1();
test01 test = new test01();
test.method2();
}
}
-
带有static的方法,既可以用“类名.”的方式访问,也可以用“引用.”的方式访问
采用引用方式调用,执行时与引用所指向的对象无关,程序会出现警告
public class test {
public static void doSome(){
System.out.println("do some");
}
public static void main(String[] args) {
test.doSome();
doSome();
test t = new test();
t.doSome();
t = null; //不构成空指针异常
t.doSome();
}
}
-
static关键字
- static修饰的方法是静态方法,static修饰的变量是静态变量
- 所有static修饰的元素都是静态的,可以用“类名.”访问,也可以用“引用.”访问(不推荐)
- static修饰的元素都是类级别的特征,与具体对象无关
-
成员变量声明为实例变量:所有对象都有该属性,且不同对象属性不同
-
成员变量声明为静态变量:所有对象都有该属性,且所有对象属性相同
-
静态变量在类加载时初始化,在方法区开辟内存,访问时不需要创建对象
public class Chinese {
String id;
String name;
String country;
//static String country; 所有对象国籍相同,属于整个模板的特征,创建为静态变量
public Chinese(){}
public Chinese(String id,String name,String country){
//public Chinese(String id,String name);
this.id = id;
this.name = name;
this.country = country;
System.out.println(this.id + this.name + this.country);
//可以改为System.out.println(this.id + this.name + Chinese.country);
}
public static void main(String[] args) {
Chinese zhangsan = new Chinese("1","zhangsan","China");
//Chinese zhangsan = new Chinese("1","zhangsan");
Chinese lisi = new Chinese("2","lisi","China");
//Chinese lisi = new Chinese("2","lisi");
}
}
-
可以用static关键字定义“静态代码块”:
- 语法格式:
static{
java语句;
}
- 静态代码块在类加载时执行,且只执行一次
- 静态代码块在一个类中可以编写多个,遵循自上而下的顺序依次执行
- 用途:例如要求在类加载时完成日志记录(静态代码块执行时刻为类加载时刻)
- 通常在静态代码块中完成预备工作,完成数据的准备工具(例如:初始化连接池、解析XML配置文件)
public class Static01 {
static {
System.out.println("类加载1"); //输出顺序:1
}
static {
System.out.println("类加载2"); //输出顺序:2
}
public static void main(String[] args) {
System.out.println("主方法"); //输出顺序:3
}
}
- 实例语句块/实例代码块(很少用)
- 实例代码块可以编写多个,自上而下依次执行
- 实例代码块在构造方法执行前执行,构造方法执行一次,实例代码块也对应执行一次
- 实例代码块执行的时机被称为对象初始化时机
public class StaticTest {
public StaticTest(){ //构造函数
System.out.println("缺省构造器"); //输出顺序:4
}
{ //实例代码块
System.out.println(1); //输出顺序:2
}
{
System.out.println(2); //输出顺序:3
}
public static void main(String[] args) {
System.out.println("main"); //输出顺序:1
new StaticTest();
}
}
- 重新理解System.out.println(String[] args);主方法
- public表示公开的,在任何位置都能访问
- static表示静态的,使用“类名.”方式即可访问
- void表示main方法执行结束后返回值类型为空
- main是main方法的方法名
- (String[] args)是main方法的形参列表
public class Main {
public static void main(int i) { //方法重载
System.out.println(i);
}
public static void main(String args) {
System.out.println(args);
}
public static void main(String[] args) {
Main.main(10); //输出10
Main.main("hello world"); //输出hello world
}
}
-
静态方法:
方法描述的是动作,当不同对象执行某一动作结果相同时,该动作提升为类级别/模板级别的动作
静态方法中无法直接访问实例变量和实例方法
public class StsticTest01 {
int i; //实例变量
public void doSome(){ //实例方法
}
public static void main(String[] args) { //静态方法(静态上下文)
System.out.println(i); //编译失败
doSome(); //无法从静态上下文中引用非静态 方法
}
}
“工具类”一般使用静态方法:
public class Math {
public static int sum(int a,int b){
return a + b;
}
public static int divide(int a,int b){
return a / b;
}
public static void main(String[] args) {
System.out.println(sum(10,20));
System.out.println(divide(40,20));
}
}
[修饰符列表] class 类名 extends 父类名{
类体 = 属性 + 方法
}
-
java中的继承只支持单继承,一个类只能继承一个类,不能同时继承多个类(在c++中支持多继承)
-
继承中的术语:(若B类继承A类)
A类称为:父类、基类、超类、superclass
B类称为:子类:派生类、subclass
-
子类继承父类的数据:私有数据、构造方法不继承,其他数据都可以被继承
-
虽然java只支持单继承,但一个类也可以间接继承其他类,例如:
C extends B{}
B extends A{}
A extends T{} //C类直接继承B类,但是C类间接继承T、A类
- java中假设一个类没有显示继承任何类,该类默认继承JavaSE库中的java.lang.Object类
public class ExtendsTest { //这个类没有定义任何方法
public static void main(String[] args) {
ExtendsTest a = new ExtendsTest();
String b = a.toString(); //编译通过
//说明该类继承了Object类,可以调用toString方法
System.out.println(b); //输出ExtendsTest@1b6d3586
}
}
- 继承举例:
public class Account {
private String no;
private double balance;
public Account() {}
public Account(String no, double balance) {
this.no = no;
this.balance = balance;
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
public class CreditAccount extends Account{ //继承Account类的公开方法
private double credit;
public CreditAccount(String no, double balance, double credit) {
super(no, balance); //自动生成
this.credit = credit;
}
public double getCredit() {
return credit;
}
public void setCredit(double credit) {
this.credit = credit;
}
}
public class ExtendsTest {
public static void main(String[] args) {
CreditAccount act = new CreditAccount("001",100.0,99.9);
System.out.println(act.getNo() + "," + act.getBalance() + "," + act.getCredit()); //输出001,100.0,99.9
}
}
public class ExtendsTest0 {
public static void main(String[] args) {
C c = new C();
c.doSome(); //这里调用的doSome方法是从B类中继承的
//输出do some B
}
}
class A{
public void doSome(){
System.out.println("do some A");
}
}
class B extends A {
public void doSome(){
System.out.println("do some B");
}
}
class C extends B {
}
-
方法覆盖(override)
- 方法覆盖又被称为方法重写:override(官方)/overwrite
- 当父类中的方法无法满足子类的业务需求,子类有必要对父类继承来的方法重新编写,称为方法重写/方法覆盖
- 方法覆盖发生在具有继承关系的父子类之间,覆盖只针对方法,不谈属性
- 构成方法覆盖:返回值类型相同、方法名相同、形参列表相同
- 子类的方法访问权限不能更低,可以更高(若父类方法为public,子类在方法覆盖时只能是public)
- 抛出异常不能更多,可以更少
- 私有方法、构造方法不能继承,所以不能覆盖;静态方法不存在覆盖
public class Animal {
public void move(){ //定义父类方法move
System.out.println("动物会移动");
}
}
public class Cat extends Animal{ //继承父类Animal
public void move() { //覆盖从Animal类继承来的move方法
System.out.println("猫会跑");
}
}
public class Bird extends Animal{
public void move() {
System.out.println("鸟会飞");
}
}
public class Chicken extends Bird{ 继承父类Bird
public void move() { //覆盖从Bird类继承来的move方法
System.out.println("鸡不会飞");
}
}
public class OverrideTest {
public static void main(String[] args) {
Animal a = new Animal();
a.move(); //调用对象a的move方法,输出动物会移动
Cat c = new Cat();
c.move(); //输出猫会跑
Bird b = new Bird();
b.move(); //输出鸟会飞
Chicken ch = new Chicken();
ch.move(); //输出鸡不会飞
}
}