一、基本语法
编写 Java 程序时,应注意以下几点:
- 大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的。
- 类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如 MyFirstJavaClass 。
- 方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
- 源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为 .java。(如果文件名和类名不相同则会导致编译错误)。
- 主方法入口:所有的 Java 程序由 public static void main(String[] args) 方法开始执行。
接口(interface)
在 Java 中,接口可理解为对象间相互通信的协议。接口在继承中扮演着很重要的角色。
接口只定义派生要用到的方法,但是方法的具体实现完全取决于派生类。
二、类与对象
1、构造方法
每个类都有构造方法。如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。
public class Puppy{
public Puppy(){
}
public Puppy(String name){
// 这个构造器仅有一个参数:name
}
}
那么在使用new创建对象的时候,可以选择传入参数或不传入参数
在同一目录下的不同类(文件)可以直接引用
2、import语句
在 Java 中,如果给出一个完整的限定名,包括包名、类名,那么 Java 编译器就可以很容易地定位到源代码或者类。import 语句就是用来提供一个合理的路径,使得编译器可以找到某个类。
import java.io.*
//将会命令编译器载入 java_installation/java/io 路径下的所有类
3、package语句
为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。
包的作用
- 1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
- 2、如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
- 3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
例如某个Something.java文件:
package net.java.util; //那么它的路径应该是 net/java/util/Something.java 这样保存的。
public class Something{
...
}
import 关键字
还可以使用import载入包中的类:
import payroll.Employee;
//路径为 payroll/Employee.java
如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。
三、类与对象(反射)
1、类是什么?
万事万物皆是对象
class Foo{}
//foo 是 Foo的实例对象,那么Foo是谁的对象?
Foo foo = new Foo();
//任何一个类都是Class的实例对象,总共有三种表达方式
//Foo 是 Class的对象,c1就是Foo的类类型(class type)
//已知类的情况下,可以得到它的类类型(所有类都有一个隐含的静态成员变量class)
//c1也是Class类的实例对象
Class c1 = Foo.class;
//c2也是Foo的类类型
//已知类的对象,也可以得到它的类类型
Class c2 = foo.getClass();
//c3也是Foo类的类类型
//需要有异常处理
Class c3=null;
try {
c3=Class.forName("com.my.package1.Foo");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//我们完全可以通过类的类类型来创建该类的实例对象
//需要有异常处理
try {
Foo foo1=(Foo) c1.newInstance(); //使用该方法的前提是需要有无参数的构造方法
foo1.print();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
通过上面的一些方法构造及使用,我们能对类有一个更深的了解
2、动态加载类
有动态加载类,肯定就有静态加载类
静态加载类
静态加载(new)就是在编译时就需要加载完成的类,若是没加载成功,程序就无法运行
//Test.java
class Test{
public static void main(String[] args){
if("Word".equals(args[0])){
Word word=new Word();
word.start(); //若Word类加载失败,程序报错无法运行(即使Excel类加载成功)
}
if("Excel".equals(args[0])){
Excel excel =new Excel();
excel.start();
}
}
}
若是上述代码后期需要修改,我们就需要找到类一步步去修改,然后再添加相关代码在其中
动态加载类
动态加载可以解决静态加载的问题,除了无法加载的类报错外,其他的代码可以运行
比如:今后我需要对这段代码进行修改,如果使用静态加载的方式,就需要修改和添加相关代码在已完成的文件中(会很麻烦)
动态加载后,我们只需要从外部添加一个类来继承并修改其中的方法就好了
//Test.java
class TestBetter{
public static void main(String[] args){
try {
//动态加载类
Class c=Class.forName(args[0]); //args[0]返回命令行输入的字符串,返回输入的类的类类型
//通过类类型创建对象
Office oa = (Office)c.newInstance();
oa.start();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//Office.java
interface Office{
public void start();
}
//Hello.java
class Hello implements Office{
public void start(){
System.out.println("Hello...start...");
}
}
Test.java 实现了动态加载类,只有在使用后出现问题才会报错
Office.java 使用了接口,相当于动态加载类的一个过渡,若修改,只需要添加相关方法名就行了
Hello.java 是我们需要的实现功能的类,我们借助接口,添加我们想要的功能
这样一个动态加载类就完成了,后期修改,只需要外部添加一个类(文件)就行了
这就类似于软件更新一样,动态地加载新的东西,而不是在原有的文件上进行修改
3、获取类信息
基本的数据类型
Class c1=int.class; //int的类类型
Class c2=String.class; //String类的类类型
Class c3=void.class; //void的类类型
Class c4=abc.class; //abc类的类类型
//一个类的类类型就能获得该类的类名
System.out.println(c2.getName()); //java.lang.String
System.out.println(c2.getSimpleName()); //String(不带包名)
Class类的基本API操作
//Test.java
import java.lang.reflect.Method;
public class Test {
/**
* 打印所属类(Object)的信息,包括类的成员函数、成员变量
* @param obj
*/
public static void printClassMessage(Object obj){
//首先获取类的类类型(共三种)
//Foo.class; foo.getClass(); Class.forName("类名");
Class c=obj.getClass(); //obj(Object) 传递的是哪个子类的对象,c就是哪个子类的类类型
//获取类的名称
System.out.println(c.getName());
//Method是所有方法的对象,一个成员方法就是一个Method对象
Method[] ms = c.getMethods(); //该方法获取所有public方法,包括从父类继承而来的
Method[] ms1 = c.getDeclaredMethods(); //获取所有该类自己声明的方法,不问访问权限
//获取方法
//该数组存储的是方法的类类型(Method类的对象)
for(int i=0;i<ms.length;i++){
//方法返回值类型,得到的是数据类型的类类型
Class returnType = ms[i].getReturnType();
System.out.print(returnType.getName()+" ");
//得到方法名称
System.out.print(ms[i].getName()+"(");
//获取参数类型,得到的是参数列表的返回值类型的类类型
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1:paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
}
//ClassDemo1.java
public class ClassDemo1 {
public static void main(String[] args){
String s = "hello";
Test.printClassMessage(s); //就可以获得String类的所有方法
}
}
4、获取成员变量信息
成员变量也是对象:java.lang.reflect.Field
Field类封装了关于成员变量的操作
public static void printClassMessage(Object obj){
Class c=obj.getClass();
Field[] fs=c.getFields(); //获取所有public的成员变量的信息
Field[] fs2=c.getDeclaredFields(); //获取该类自己声明的成员变量的信息
for (Field field : fs ) {
//成员变量数据类类型
Class fieldType = field.getType();
//获取类类型的名字(例如int、String)
String typeName = fieldType.getName();
//获取成员变量的名称
String fieldName = field.getName();
System.out.println(typeName+" "+fieldName);
}
获取构造函数的信息
构造函数也是对象
java.lang.Constructor
public static void printConMessage(Object obj){
Class c = obj.getClass();
//获取所有public对象的构造函数
Constructor[] cs = c.getConstructors();
//获取所有自己声明的构造函数
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor:cs
) {
System.out.print(constructor.getName()+"(");
//获取构造函数的参数列表,返回的是参数数据类型的类类型
Class[] paramTypes = constructor.getParameterTypes();
for (Class class1:paramTypes
) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
想要获取一个类的类信息,就要获取到它的类类型,从而就能获取到所有关于类的信息
5、方法反射的操作
反射可以绕过编译阶段
public class Test {
public static void main(String[] args) {
A a1 = new A();
Class c1=a1.getClass();
/**
* 获取方法 由名称和参数来决定
* getMethod 获取public的方法
* getDeclaredMethod 获取自己声明的方法
*/
try {
Method m = c1.getMethod("print", int.class, int.class);
//Method m = c1.getMethod("print",new Class[]{int.class,int.class});
//方法的反射操作
//正常:a1.print(10,30); 方法的反射操作使用m对象来进行方法调用,和a1.print效果相同
//method.invoke 方法有返回值(调用的方法无返回值返回null,有返回值就返回返回值)
Object o = m.invoke(a1,10,30);
//Object o = m.invoke(a1,new Object[]{10,30});
System.out.println("===========");
Method m2 = c1.getMethod("print", String.class, String.class);
//Method m2 = c1.getMethod("print",new Class[]{String.class,String.class});
o=m2.invoke("print","qwe","qqq");
//o=m2.invoke("print",new Object[]{"qwe","qqq"});
System.out.println("=============");
Method m3=c1.getMethod("print");
//Method m3=c1.getMethod("print",new Class[]{});
o=m3.invoke("print");
//o=m3.invoke("print",new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
}
}
class A{
public void print(int a,int b){
System.out.println(a+b);
}
public void print(String a,String b){
System.out.println(a.toUpperCase()+","+b.toLowerCase());
}
public void print(){
System.out.println("helloworld");
}
}
反射对泛型的绕过:
/**
* 泛型是为了防止错误输入,但是只在编译阶段起作用
*/
ArrayList list1 = new ArrayList();
//使用泛型
ArrayList<String> list2 = new ArrayList<String>();
//获得集合的类类型
Class c2 = list1.getClass();
Class c3 = list2.getClass();
System.out.println(c2 == c3); //说明编译之后的集合的泛型是去泛型化的
//利用反射调用集合方法
try {
Method m4 = c2.getMethod("add", Object.class);
m4.invoke(list1,"123");
System.out.println(list1);
} catch (Exception e) {
e.printStackTrace();
}
try {
Method m5 = c3.getMethod("add", Object.class);
m5.invoke(list2,123); //这里就绕过了泛型的限制
System.out.println(list2);
} catch (Exception e) {
e.printStackTrace();
}
Java中集合的泛型是防止错误输入的,但是只在编译阶段起作用,绕过编译就无效了,而反射的操作就是在编译之后操作的,可以绕过泛型的限制
6、Class 和 Object 是什么关系?
emmm,看完视频后,在底下评论区看到了这个问题,百度之后,就记录一下。
在实现了Class类时,是继承了Object类,然后编译成.class字节码,这样就能被JVM加载了。
虚拟机加载了所有类,这些类在虚拟机中都能被Class类所描述(所以说它是编译之后的操作)。
三、修饰符
1、访问控制修饰符
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
- default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
- public : 对所有类可见。使用对象:类、接口、变量、方法
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
- 本文链接:http://siii0.github.io/java%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。