整理自 韩顺平零基础30天学Java
变量作用域
- Java编程中,主要的变量就是属性(成员变量)和局部变量。
- 局部变量一般是指在成员方法中定义的变量。
- 全局变量:也就是属性,作用域为整个类体;
局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块。 - 全局变量可以不赋值,直接使用,因为有默认值;
局部变量必须赋值后,才能使用,因为没有默认值。
注意
- 属性和局部变量可以重名,访问时遵循就近原则。
- 在同一个作用域中(如同一个成员方法),两个局部变量不能重名。
- 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。
局部变量的生命周期较短,伴随着它的代码块的执行而创建,伴随者代码块的结束而销毁,即在依次方法调用过程中。 - 作用域不同
全局变量:可以被本类使用,或其他类使用(通过对象调用);
局部变量:只能在本类中对应的方法中使用。 - 修饰符不同
全局变量/属性可以加修饰符;
局部变量不可以加修饰符。
构造方法 / 构造器
[修饰符] 方法名(形参列表){
方法体;
}
类的一种特殊方法,完成对新对象的初始化。
- 构造器的修饰符可以默认。
- 构造器没有返回值。
- 方法名 和 类 名字必须一样。
- 参数列表 和 成员方法 一样的规则。
- 构造器的调用由系统完成。
public class Constructor {
public static void main(String[] args){
//new一个对象时,直接通过构造器指定名字和年龄
Person p1 = new Person("smith", 80);
System.out.println("name=" + p1.name + "age=" + p1.age);
}
}
class Person {
String name;
int age;
//构造器 名称和类名一致
public Person(String pName, int pAge){
name = pName;
age = pAge;
}
}
细节
- 一个类可以定义多个不同的构造器,即构造器重载。
- 构造器是完成对象的初始化,并不是创建对象。
- 在创建对象时,系统自动地调用该类的构造方法。
- 如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造方法(也叫默认构造方法)。比如Person(){},使用javap指令 反编译看看。
- 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器。
Dog dog = new Dog(); //使用的是默认的无参构造器
Dog(){} //显示定义完无参构造器,上述代码又可以使用了。
对象创建的流程分析(面试题)p245
- 加载Person类信息(Person.class),只会加载一次。
- 在堆中分配空间(地址)。
- 完成对象初始化。
- 默认初始化。
- 显式初始化。
- 构造器初始化。
- 把对象在堆中的地址返回给p。(p是对象名,也可以理解为对象的引用)
this关键字
构造器的输入参数名不是非常号,如果能将pName改为name就好了,BUT变量的作用域原则,会导致name的值就是null,所以引入this关键字。
Java虚拟机会给每个对象分配this,代表当前对象。
p.s:hashCode()不是地址,会针对不同对象返回不同的整数,可以用来区分。
注意
- this关键字可以用来访问本类的属性、方法、构造器。
- this用于区分当前类的属性和局部变量。
- 访问成员方法的语法:this.方法名(参数列表);
- 访问构造器的语法:this(参数列表); 注意只能在构造器中调用另一个构造器(必须放在第一条)。
- this不能在类定义的外部使用,只能在类定义的方法中使用。
public class TestPerson{
public static void main(String[] args){
Person p1 = new Person("mary",30);
Person p2 = new Person("lucy",30);
System.out.println(p1.compareTo(p2));
}
}
class Person{
String name;
int age;
//构造器
public Person(String name, int age){
this.name = name;
this.age = age;
}
public boolean compareTo(Person p){
return this.name.equals(p.name) && this.age == p.age;
}
}
习题
package chapter01;
public class D6 {
public static void main(String[] args) {
// 类A01,方法max,求某个double数组的最大值并返回
// double[] arr1 = {1.0,2.0,3.0,4.0,5.0,6.0};
double[] arr1 = {};
A01 a01 = new A01();
Double res = a01.max(arr1);
if (res != null){
System.out.println("最大值是" + a01.max(arr1));
}else{
System.out.println("arr输入有误!");
}
}
}
class A01{
public Double max(double arr[]){ //Double是一个包装类
// 保证arr至少有一个元素 健壮性
if (arr != null && arr.length > 0){ //arr是引用类型可以为空,但null.length会报错
double m = 0.0;
for (int i = 0; i < arr.length; i++){
if (arr[i] > m)
m = arr[i];
}
return m;
}else{
return null;
}
}
}
返回为null,不能直接返回给基本数据类型double,所以要改成Double类。
public class Test {
int count = 9;
public void count1() {
count = 10;
System.out.println("count1=" + count); //10
}
public void count2() {
System.out.println("count1=" + count++);
}
public static void main(String args[]) {
new Test().count1(); //匿名对象,只能调用一次
Test t1 = new Test();
t1.count2(); //9
t1.count2(); //10
}
}
构造器复用
class Employee{
String name;
char gender;
int age;
String job;
double sal;
public Employee(String job, double sal){
this.job = job;
this.sal = sal;
}
public Employee(String name, char gender, int age){
this.name = name;
this.gender = gender;
this.age = age;
}
public Employee(String job, double sal, String name, char gender, int age){
this(name, gender, age);
this.job = job;
this.sal = sal;
}
}
Random r = new Random();
int n = r.nextInt(3); //返回0-2的随机数
idea快捷键
- ctrl + alt + 向下光标 = 复制一行
- ctrl + d = 删除一行
- alt + / = 注释
- ctrl + alt + l = 格式化代码
- alt + r = 快速运行
- fn + alt + insert = 快速生成构造器
- ctrl + h = 查看类的继承关系
- 光标在方法上 + ctrl + b = 定位到方法 或直接 ctrl + 单击
- .var = 自动分配变量名。e.g.new Scanner(System.in).var
- fori:for循环模板快捷键
包
类似:创建不同文件夹来保存类文件。
作用
- 区分相同名字的类。
- 当类很多时,可以很好地管理类。
- 控制访问范围。
基本语法
package com.duoruimi //关键字 包名
命名
规则
只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字。
规范
小写字母+小圆点。
com.公司名.项目名.业务模块名
常用
- java.lang.*:lang包是基本包,默认引入,不需要再引入。
- java.util.*:util包,系统提供的工具包,工具类,使用Scanner。
- java.net.*:网络包,网络开发。
- java.awt.*:是做java的界面开发,GUI。
细节
import java.util.Scanner; //只引入java.util包下的Scanner类
import java.util.*; //引入包下的所有类
Arrays.sort():自动排序
访问修饰符
- public:对外公开。
- protected:对子类和同一个包中的类公开。
- 默认级别:向同一个包的类公开。
- privateL只有类本身可以访问,对外不公开。
注意事项
- 修饰符可以用来修饰类中的属性、成员方法以及类。
- 只有默认的和public才能修饰类,并且遵循上述访问权限的特点。
- 成员方法的访问规则和属性完全一致。
面向对象编程三大特征
封装、继承和多态。
封装
把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作。
好处
- 隐藏实现细节。
- 可以对数据进行验证,保证安全合理。
实现步骤
- 对属性私有化。【不能直接修改属性】
- 提供一个公共的set方法,用于对属性判断并赋值。
- 提供一个公共的get方法,用于获取属性的值。
- 封装和构造器结合。
package chapter01;
public class D6 {
public static void main(String[] args) {
Person person = new Person();
person.setName("jack");
person.setAge(30);
person.setSalary(300000);
System.out.println(person.info);
}
class Person {
public String name;
private int age;
private double salary;
//构造器,使封装的防护机制失效;可以将set方法写在构造器中,这样仍然可以验证数据
public Person(){
}
public Person(String name, int age, double salary) {
//this.name = name;
//this.age = age;
//this.salary = salary;
setName(name);
setAge(age);
setSalary(salary);
}
public void setName(String name){
if(name.length() >=2 && name.length() <=6) {
this.name = name;
}else {
System.out.println("名字的长度不对,需要2-6个字符,默认名字")
this.name = "某某";
}
}
public String getName(){
return name;
}
public void setAge(String age){
if (age >= 1 && age <= 120){
this.age = age;
}else{
System.out.println("你设置的年龄不对,需要在1-120,默认为18");
this.age = 18;
}
}
public int getAge(){
return age;
}
public void setSalary(double salary){
this.salary = salary;
}
public double getSalary){
//可以增加对用户的权限验证
return salary;
}
public String info(){
return "信息为 name" + name + "age=" + age + "salary=" + salary;
}
}
继承
继承可以解决代码复用。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
基本语法
class 子类 extends 父类{
}
- 子类自动拥有父类定义的属性和方法。
- 父类又叫超类、基类。
- 子类又叫派生类。
好处
- 代码的复用性大大提高。
- 代码的可扩展性和维护性提高了。
细节
- 子类继承了所有的属性和方法,但是私有属性不能在子类直接访问,要通过父类提供公共的方法去访问。
- 子类必须调用父类的构造器,完成父类的初始化。(默认调用父类的无参构造器)
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器(如:super(参数列表))完成对父类的初始化工作,否则编译不会通过。
- super必须放在构造器第一行。
- super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器中。
- java所有类都是Object类的子类,Object是所有类的基类。
- 父类构造器的调用不限于直接父类,将一直往上追溯到Object类(顶级父类)。
- 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。
如何让A继承B和C? A继承B,B继承C。 - 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系。
继承按照查找关系返回信息
- 如果子类有这个属性,并且可以访问,则返回信息。
- 如果子类没有,就看父类有没有。
- 以此类推,直到Object。
- 都没有,报错。
super关键字
super代表父类的引用,用于访问父类的属性、方法、构造器。
语法
- 访问父类的属性,但不能访问父类的private属性。
super.属性名; - 访问父类的方法,不能访问父类的private方法。
super.方法名(参数列表); - 访问父类的构造器。
super(参数列表); 只能放在构造器的第一句,只能出现一句。
便利和细节
- 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)。
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果。
super直接找父类,this和直接访问,从自身开始往上找。 - super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。
super和this的比较
区别点 | this | super |
---|---|---|
访问属性 | 访问本类中的属性,如果本类没有此属性,则从父类中继续查找 | 从父类开始查找属性 |
调用方法 | 访问本类中的方法,如果本类没有此方法则从父类继续查找 | 从父类开始查找方法 |
调用构造器 | 调用本类构造器,必须放在构造器首行 | 调用父类构造器,必须放在子类构造器的首行 |
特殊 | 表示当前对象 | 子类中访问父类对象 |
方法重写/覆盖(override)
子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,就说子类的这个方法覆盖了父类的方法。
满足条件
- 子类的方法的参数、方法名称,要和父类方法的参数、方法名称完全一致。
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类。
比如,父类返回类型是Object,子类返回类型是String。 - 子类方法不能缩小父类方法的访问权限。
public > protected > 默认 >private。
重写与重载的比较
名称 | 发生范围 | 方法名 | 参数列表 | 返回类型 | 修饰符 |
---|---|---|---|---|---|
重载(overload) | 本类 | 必须一样 | 类型、个数、顺序至少有一个不同 | 无要求 | 无要求 |
重写(override) | 父子类 | 必须一样 | 必须相同 | 子类返回类型和父类返回类型一致,或是其子类 | 子类方法不能缩小父类方法的访问范围 |