【Java】基础知识

2020/08/03
考虑考算法岗的红海,正式打算下海Java算法岗了。

文章目录

  • Java基础知识
    • 变量
    • 数据类型
    • 文档注释
    • 运算符
    • 数组
  • 面向对象
    • 方法
    • 类和对象
    • 继承 `extends`
      • 方法重写
      • 访问修饰符
      • super 关键字
      • final关键字
    • 多态
    • 抽象类与抽象方法 `abstract`
    • 接口`interface`
    • 内部类
      • 成员内部类(private)
      • 静态内部类(static)
      • 方法(局部)内部类(不能修饰)
      • 匿名内部类
      • 转载关于匿名的分析

Java基础知识

  • JVM: java虚拟机。

  • JDK:Java开发工具包

  • JRE:Java运行时环境
    【Java】基础知识_第1张图片

  • 源文件:Hello.java ——>经过编译器得到字节码文件 Hello.class文件——>在经过解释器,从而实现运行。字节码文件是可以跨平台的。字节码文件可以通用,JVM 是特定于平台的.

  • 文本编辑java:可以用txt文件完成java的编写,在cmd中进行到相应的文职,运行javac Helloworld.java,这样会在相应位置生成一个Helloworld.class文件。再次java Helloworld
    这里javac命令是对原始文件进行编译,java的命令对字节码进行执行。

变量

Java 语言定义了以下 4 种变量 :

  • 实例变量
    实例变量在类范围内声明。声明时不使用 static 关键字修饰,因此也称为非静态字段。需要通过 new 关键字实例化出对象,通过对象才能访问其实例变量(也称实例属性)。
  • 类变量
    类变量在类范围内使用 static 关键字修饰,因此类变量又被称为静态字段、静态变量。可以被类内所有的实例共享。可以直接通过类名 + . 点运算符 + 类变量名的方式调用。
  • 局部变量
    局部变量只可以对声明它们的方法可见。方法返回后,它们将被销毁。
  • 参数(见代码中 parameter 和 args)

数据类型

【Java】基础知识_第2张图片

需要注意char是单一字符,Java 中的 char 类型除了可表示标准的 ASCII 外,还可以表示一个 Unicode 字符;String是字符串,并不是基本类型。需要注意的是,long 类型的值后面要以大写字母 L 或小写字母 l 结尾,long a = 10L。需要注意的是,float 类型的值必须要以大写字母 F 或小写字母 f 结尾,float a = 1.2f,也可以使用科学计数法double = d = 1.23e2

// 需要特别注意,String的是双引号,char是单引号
		String name="爱慕课";
		char sex='男';

【Java】基础知识_第3张图片

java是支持自动类型转换,但是要求目标类型与源类型兼容,如double兼容int型。或者目标类型大于源类型,double的字节长度是大于int的。同时也是可以使用强制类型转换的,如,

double heightAvg1=176.2;
int heightAvg2 = (int)heightAvg1;

利用final定义常量
常量的定义一般采用大写字母final PI = 3.014;

文档注释

PS:使用文档注释时还可以使用 javadoc 标记,生成更详细的文档信息:

   @author 标明开发该类模块的作者

   @version 标明该类模块的版本

   @see 参考转向,也就是相关主题

   @param 对方法中某参数的说明

   @return 对方法返回值的说明

   @exception 对方法可能抛出的异常进行说明

运算符

这里主要说明一些和python中不太一样的

  1. && 这个是and
  2. || 这个是or
  3. ! 这个是逻辑反
  4. 布尔表达式 ? 表达式1 :表达式2三元运算符,如果判断条件为真,执行第一个部分,否则执行第二个。
String mark = (score>=60)?"及格":"不及格";
  1. instanceof将对象与指定类型进行比较,检查对象是否是一个特定类型(类类型或接口类型)。注意,instanceof 运算符不能用于操作基本数据类型
// 格式
( Object reference variable ) instanceof  (class/interface type)
boolean b = name instanceof String; 
  1. 运算符的优先级
    【Java】基础知识_第4张图片

数组

  1. java中数组的初始化:
    int[] scores = { 78, 93, 97, 84, 63 };或者int[] scores = new int[]{ 78, 93, 97, 84, 63 };
  2. 数组长度:
    nums.length
  3. 调用库函数:Arrays,导入:import java.util.Arrays
  • 实现排序:Arrays.sort(nums)
  • 将数组转换为字符串:Arrays.toString(数组名);
  1. foreach循环
for (int e:elements){
}

面向对象

面向对象的三大特征:封装、继承、多态。

方法

  • 方法名:访问修饰符 返回类型 方法名(参数列表),其中访问修饰符有 4 种情况:publicprivateprotected,也可以省略(default)。
  • 方法参数:可以不确定得到几个参数,采用可变参数public void sun(int... n)。这中方法最后进来是一个int数组。当方法的参数列表有两个或两个以上参数时,可变参数一定要放在最后。将数组传递给可变参数列表。
  • 需要注意的就是java中只能返回一个内容,如果想返回多个需要new object[]{a, b}
  • 方法的传值:本质上java是值引用。基本类型参数的传递,是调用方值的复制。双方各自的后续修改,互不影响。引用类型参数的传递,调用方的变量,和接收方的参数变量,地址指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方。

类和对象

  1. 对象实例化:
    关于对象的实例化过程,可以分为两部分:
  • 声明对象:在内存的栈空间中执行。ImoocStudent student;
  • 实例化对象:在内存的堆空间执行。new ImoocStudent();。
  1. 调用
// 调用属性
对象名.属性名;
// 调用方法
对象名.方法名();
  1. 构造器:public形式,方法名与类名同名。this关键字的使用,表示当前实例。
  2. 封装:构建public的getter和setter方法,但是类内的变量定义为private,保证不会在类外被访问。

继承 extends

继承:Java 中的继承为单一继承,也就是说,一个子类只能拥有一个父类,一个父类可以拥有多个子类。

另外,所有的 Java 类都继承自 Java.lang.Object,所以 Object 是所有类的祖先类。子类一旦继承父类,就会继承父类所有开放的特征,不能选择性地继承父类特征

方法重写

如果一个类从它的父类继承了一个方法,如果这个方法没有被标记为 finalstatic,就可以对这个方法进行重写。重写的好处是:能够定义特定于子类类型的行为,这意味着子类能够基于要求来实现父类的方法。重写一般要加上标识,@override

方法重写规则

  • 重写方法的参数列表应该与原方法完全相同;返回值类型应该和原方法的返回值类型一样或者是它在父类定义时的子类型;
  • 重写方法访问级别限制不能比原方法高。例如:如果父类方法声明为公有的,那么子类中的重写方法不能是私有的或是保护的。具体限制级别参考访问修饰符;
  • 只有被子类继承时,方法才能被重写;
  • 一个方法被定义为 static,将使其不能被重写,但是可以重新声明;
  • 一个方法不能被继承,那么也不能被重写;
  • 和父类在一个包中的子类能够重写任何没有被声明为 private 和 final 的父类方法;
  • 和父类不在同一个包中的子类只能重写 non-final 方法或被声明为 public 或 protected 的方法;
  • 一个重写方法能够抛出任何运行时异常,不管被重写方法是否抛出异常。然而重写方法不应该抛出比被重写方法声明的更新更广泛的已检查异常。重写方法能够抛出比被重写方法更窄或更少的异常;
  • 构造方法不能重写。

方法重写和方法重载的区别

Java 中的方法重写(Overriding)是说子类重新定义了父类的方法。方法重写必须有相同的方法名,参数列表和返回类型。覆盖者访问修饰符的限定大于等于父类方法。

而方法重载(Overloading)发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。

访问修饰符

Java 一共提供了 4 种访问修饰符:
【Java】基础知识_第5张图片

super 关键字

super 是用在子类中的,目的是访问直接父类的变量或方法。注意:

  • super 关键字只能调用父类的 public 以及 protected 成员,可以是属性也可以是方法;
  • super 关键字可以用在子类构造方法中调用父类构造方法,super(name)
  • super 关键字不能用于静态 (static) 方法中。

final关键字

一般放在static之后。可以作用于子类,方法,变量。

  1. final作用于子类,这个类是无法别继承的。
  2. final作用于方法,这个方法是无法被重写的。
  3. final作用于变量,这个变量被初始化之后无法修改。

多态

在面向对象中最常用的多态性发生在当父类引用指向子类对象时。在面向对象编程中,所谓多态意指相同的消息给予不同的对象会引发不同的动作。换句话说:多态意味着允许不同类的对象对同一消息做出不同的响应。

实现多态的三个条件:1.满足继承关系;2.要有重写;3.父类引用指向子类对象。

例子:

在代码中,Pet dog = new Dog();、Pet cat = new Cat(); 这两个语句,把 Dog 和 Cat 对象转换为 Pet 对象,这种把一个子类对象转型为父类对象的做法称为向上转型父类引用指向了子类的实例也就实现了多态。子类的对象可以赋值给父类对象。

public void main(String[] args) {
  	// 分别实例化三个对象,并且保持其类型为父类Pet
  	Pet pet = new Pet();
  	Pet dog = new Dog();
  	Pet cat = new Cat();
  	// 调用对象下方法
  	pet.eat();
  	dog.eat();
  	cat.eat();
}

注意:不能将父类对象转换为子类类型,也不能将兄弟类对象相互转换。
为提高向下转型的安全性,先进行instanceof的判断。

Pet pet = new Cat();
if (pet instanceof Cat) {
		// 将父类转换为子类
		Cat cat = (Cat) pet;
}

抽象类与抽象方法 abstract

当某个父类只知道其子类应该包含什么方法,但不知道子类如何实现这些方法的时候,抽象类就派上用场了。使用抽象类还有一个好处,类的使用者在创建对象时,就知道他必须要使用某个具体子类,而不会误用抽象的父类,因此对于使用者来说,有一个提示作用。

子类在继承了抽象类之后,必须要重写方法。

一个抽象类不能直接实例化,但类的其他功能依然存在;既然不能被实例化,那么它必须被继承才能被使用。

abstract class Pet {
    abstract void eat();
}

抽象方法不能是finalstaticnative 的;并且抽象方法不能是私有的,即不能用 private 修饰。因为抽象方法一定要被子类重写,私有方法、最终方法以及静态方法都不可以被重写,因此抽象方法不能使用 privatefinal 以及static 关键字修饰。而native 是本地方法,它与抽象方法不同的是,它把具体的实现移交给了本地系统的函数库,没有把实现交给子类,因此和abstract 方法本身就是冲突的。

接口interface

关键字 interface 修饰的class就是一个接口。接口定义了一个行为协议,可以由类层次结构中任何位置的任何类实现。接口中定义了一组抽象方法,都没有具体实现,实现该接口的类必须实现该接口中定义的所有抽象方法

public interface Person {
  	final String NAME = "我是Person接口中的常量";
	void walk();
  	void run();
}

一个类是可以继承多个接口的。继承接口的关键字implements。继承接口之后,要重写接口要求的方法。当一个实现类存在 extends关键字,那么implements关键字应该放在其后:public class MyClass extends SuperClass implements MyInterface

接口之间也是可以继承的,关键词extends。值得注意的是,一个接口可以继承多个父接口。

接口中的默认方法与静态方法default:实现类可以不实现默认方法和类方法,当然也可以重写,重写的方法不需要defult。如果想要在实现类中调用接口的默认方法,可以使用接口名.super. 方法名 ()的方式调用。

public interface Person {
  	void run();
  	default void eat() {
      	System.out.println("我是默认的吃方法");
    }
}

静态方法:类中的静态方法只能被子类继承而不能被重写,同样在实现类中,静态方法不能被重写。如果想要调用接口中的静态方法,只需使用 接口名.类方法名() 的方式即可调用:

  • 接口和抽象类的区别

接口的方法默认是 public ,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法;

接口中除了 static 、final 变量,不能有其他变量,而抽象类可以;

一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口;

接口方法默认修饰符是 public ,抽象方法可以有 public 、protected 和 default 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!);

从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。

内部类

Java 中的内部类可以分为 4 种:成员内部类、静态内部类、方法内部类和匿名内部类。

成员内部类(private)

最简单的情况,class的内部生成了一个class。可以外部类是public, 内部类是private这样可以保证只能类内使用。我们可以通过 new 外部类().new 内部类()的方式获取内部类的实例对象:

成员访问:可以在内部类的中访问外部类的成员属性;如果内部类中也存在一个同名成员,那么优先访问内部类的成员;这种情况下如果依然希望访问外部类的属性,可以使用外部类名.this.成员的方式。

静态内部类(static)

静态内部类的实例化,可以不依赖外部类的对象直接创建。记着这是一个类共享的,因此没必要纠结是哪个对象的。

方法(局部)内部类(不能修饰)

只在方法中使用这个类,具有几个特点:局部内部类只能在方法内部使用;方法内不能定义静态成员;不能使用访问修饰符。

匿名内部类

匿名内部类就是没有名字的内部类。使用匿名内部类,通常令其实现一个抽象类或接口。不能有修饰,也没有构造器。

转载关于匿名的分析

1.为什么使用内部类?
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,
对于内部类都没有影响
1.1.使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,使用内部类还能够为我们带来如下特性:
(1)、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独。
(2)、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
(3)、创建内部类对象的时刻并不依赖于外围类对象的创建。
(4)、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。
(5)、内部类提供了更好的封装,除了该外围类,其他类都不能访问。

2.内部类分类:
(一).成员内部类:

public class Outer{
		private int age = 99;
		String name = "Coco";
		public class Inner{
			String name = "Jayden";
			public void show(){
				System.out.println(Outer.this.name);
				System.out.println(name);
				System.out.println(age);
			}
		}
		public Inner getInnerClass(){
			return new Inner();
		}
		public static void main(String[] args){
			Outer o = new Outer();
			Inner in = o.new Inner();
			in.show();
		}
	}
  1. Inner 类定义在 Outer 类的内部,相当于 Outer 类的一个成员变量的位置,Inner 类可以使用任意访问控制符,如 public 、 protected 、 private 等
  2. Inner 类中定义的 show() 方法可以直接访问 Outer 类中的数据,而不受访问控制符的影响,
    如直接访问 Outer 类中的私有属性age
  3. 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( );
  4. 编译上面的程序后,会发现产生了两个 .class 文件: Outer.class,Outer$Inner.class{}
  5. 成员内部类中不能存在任何 static 的变量和方法,可以定义常量:
    (1).因为非静态内部类是要依赖于外部类的实例,而静态变量和方法是不依赖于对象的,仅与类相关,
    简而言之:在加载静态域时,根本没有外部类,所在在非静态内部类中不能定义静态域或方法,编译不通过; 非静态内部类的作用域是实例级别
    (2).常量是在编译器就确定的,放到所谓的常量池了

★★友情提示:
1.外部类是不能直接使用内部类的成员和方法的,可先创建内部类的对象,然后通过内部类的对象来访问其成员变量和方法;
2.如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,可以使用 this 关键字,如:Outer.this.name

(二).静态内部类: 是 static 修饰的内部类,

  1. 静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问
  2. 如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员
  3. 创建静态内部类的对象时,不需要外部类的对象,可以直接创建内部类 对象名 = new 内部类();
public class Outer{
			private int age = 99;
			static String name = "Coco";
			public static class Inner{
				String name = "Jayden";
				public void show(){
					System.out.println(Outer.name);
					System.out.println(name);					
				}
			}
			public static void main(String[] args){
				Inner i = new Inner();
				i.show();
			}
		}

(三).方法内部类:其作用域仅限于方法内,方法外部无法访问该内部类
(1). 局部内部类就像是方法里面的一个局部变量一样,是不能有 publicprotectedprivate 以及 static 修饰符的
(2). 只能访问方法中定义的 final 类型的局部变量,因为:
当方法被调用运行完毕之后,局部变量就已消亡了。但内部类对象可能还存在,
直到没有被引用时才会消亡。此时就会出现一种情况,就是内部类要访问一个不存在的局部变量;
==>使用final修饰符不仅会保持对象的引用不会改变,而且编译器还会持续维护这个对象在回调方法中的生命周期.
局部内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数;防止被篡改数据,而导致内部类得到的值不一致

   /*
		使用的形参为何要为 final???
		 在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,
		 也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的,
		 毕竟站在程序的角度来看这两个根本就是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解
		 和不可接受的,所以为了保持参数的一致性,就规定使用 final 来避免形参的不改变
		 */
		public class Outer{
			public void Show(){
				final int a = 25;
				int b = 13;
				class Inner{
					int c = 2;
					public void print(){
						System.out.println("访问外部类:" + a);
						System.out.println("访问内部类:" + c);
					}
				}
				Inner i = new Inner();
				i.print();
			}
			public static void main(String[] args){
				Outer o = new Outer();
				o.show();
			}
		}    

(3). 注意:在JDK8版本之中,方法内部类中调用方法中的局部变量,可以不需要修饰为 final,匿名内部类也是一样的,主要是JDK8之后增加了 Effectively final 功能
http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
反编译jdk8编译之后的class文件,发现内部类引用外部的局部变量都是 final 修饰的

(四).匿名内部类:
(1). 匿名内部类是直接使用 new 来生成一个对象的引用;
(2). 对于匿名内部类的使用它是存在一个缺陷的,就是它仅能被使用一次,创建匿名内部类时它会立即创建一个该类的实例,该类的定义会立即消失,所以匿名内部类是不能够被重复使用;
(3). 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口;
(4). 匿名内部类中是不能定义构造函数的,匿名内部类中不能存在任何的静态成员变量和静态方法;
(5). 匿名内部类中不能存在任何的静态成员变量和静态方法,匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法
(6). 匿名内部类初始化:使用构造代码块!利用构造代码块能够达到为匿名内部类创建一个构造器的效果

  public class OuterClass {
            public InnerClass getInnerClass(final int   num,String str2){
                return new InnerClass(){
                    int number = num + 3;
                    public int getNumber(){
                        return number;
                    }
                };        /* 注意:分号不能省 */
            }
            public static void main(String[] args) {
                OuterClass out = new OuterClass();
                InnerClass inner = out.getInnerClass(2, "chenssy");
                System.out.println(inner.getNumber());
            }
        }
        interface InnerClass {
            int getNumber();
        }          

你可能感兴趣的:(Java入门,多态,抽象类,java,jdk,编程语言)