为什么不能自定义java.lang.String

如果直接写一个java.lang.String类,并写一个main方法,即

package java.lang;

public class String {

    public static void main(String[] args) {
        System.out.println("Hello String");
    }

}


运行之后会抛一个异常:

错误: 在类 java.lang.String 中找不到主方法, 请将主方法定义为:
   public static void main(String[] args)


这是什么原因呢?

答案先给出:Java类加载机制为代理模式,先交给其父加载器去加载,如果父加载器加载不了,则由自己加载。我们自定义的类是由系统类加载器进行加载,而它的父加载器为扩展类加载器,扩展类加载器为引导类加载器。我们定义的java.lang.String最先由引导加载器加载,而它负责加载Java核心库,但java.lang.String正是系统中的类,已经被引导加载器记载过了,所以不再加载自定义的java.lang.String。但是系统的java.lang.String没有main方法,所以出现了上面的这个异常。

另外,自定义包不能以java.xxx.xxx开头,这是一种安全机制,,如果以java开头,系统直接抛异常。


再来看以下两段代码:

片段1:


片段1执行结果:




片段2:



对于代码片段1,虽然能加载自定义的com.test.String类,但是main方法中的String对象也是自定义的,不符合main方法的定义方式,故系统抛找不到mian方法。

对于代码片段2,在main方法的定义中把String类的路径写全了,明确了,故能正常执行。



原因

这里必须要提到java在加载类的过程。Java在加载类时,采用的是代理模式,即,类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,以此类推。在说明代理模式背后的原因之前,首先需要说明一下Java虚拟机是如何判定两个java类是相同的。Java虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同,才认为两个类时相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的,如果此时试图对这两个类的对象进行相互赋值,会抛出运行时异常ClassCastException。

了解到这一点,就可以理解代理模式的设计动机了。代理模式是为了保证Java核心库的类型安全,所有的Java应用都至少需要引用java.lang.Object类,也就是说在运行时,java.lang.Object这个类需要被加载到Java虚拟机中。如果这个过程由Java应用自己的类加载器来完成的话,很可能就存在多个版本的java.lang.Object类,可是这些类之间是不兼容的。通过代理模式,对于Java核心库的类的加载工作由引导类加载器统一完成,保证了Java应用所使用的都是同一个版本的Java核心库的类,是相互兼容的。


类加载器的树状组织结构

Java中的类加载器大致可以分为两类,一类是系统提供的,另外一类则是由Java应用开发人员编写的。系统提供的类加载器主要有下面三个:

  • 引导类加载器 (bootstrap class loader):它用来加载Java的核心库,是用原生代码实现的,并不继承自java.lang.ClassLoader。
  • 扩展类加载器 (extensions class loader):它用来加载Java的扩展库。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载Java类。
  • 系统类加载器 (system class loader):它根据Java应用的类路径(CLASSPATH)来加载Java类。一般来说,Java应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来或其它。

除了系统提供的类加载器以外,开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器。

除了引导类加载器以外,所有的类加载器都有一个父类加载器。对于系统提供的类加载器来说,系统类加载器的父类加载器是扩展类加载器,而扩展类加载器的父类是引导类加载器。一般来说,开发人员编写的类加载器的父类加载器是系统类加载器。



  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
中文名: Java2游戏编程 原名: Java 2 Game Programming 作者: Thomas Petchel译者: 晏利斌 孙淑敏 邵荣 资源格式: PDF 版本: 扫描版 出版社: 清华大学出版社书号: 7302112932发行时间: 2005年08月 地区: 大陆 语言: 简体中文 简介: 内容介绍:   你经常看到有人在玩手机游戏吧,那些手机游戏基本上是用Java的。Java已经成熟了,它现在是一种开发能够多种平台上运行的中小型游戏的很好方式。本书将向读者展示用Java语言和它的类库创建2D游戏,所涉及的主题包括高速性能、双缓冲图像、动画、声音、媒体控制、I/O和网络支持等。将带领大家一步一步学习编Java游戏,最终打造属于自己的Java游戏。 目录: 第1篇 步入Java丛林:从Java2 API开始 第1章 Java2软件开发工具包 1.1 Java简史 1.2 为什么在游戏中使用Java 1.3 为Java准备系统 1.3.1 安装Java SDK 1.3.2 编译和运行Java程序 1.3.3 使用命令行 1.3.4 使用集成开发环境(IDE) 1.3.5 关于Java2文档的说明 1.4 总结 第2章 预备:学习Java2 API 2.1 Game Over! 程序 2.1.1 import语句 2.1.2 给Java代码加注释 2.1.3 Java类声明 2.1.4 Java方法声明 2.1.5 Java中的代码块 2.1.6 Java程序组成部分的关键点回顾 2.2 比特和字节:原始的Java类型 2.2.1 基本的整数类型 2.2.2 浮点类型 2.2.3 Char类型 2.2.4 布尔型 2.2.5 String类型 2.2.6 强制转换变量类型 2.2.7 Java数据类型、数组和标识符需要记忆的要点 2.3 Java中的运算符 2.3.1 赋值运算符 2.3.2 比较运算符 2.3.3 算术运算符 2.3.4 自增和自减运算符 2.3.5 更多的整数运算符 2.3.6 使用点运算符 2.3.7 instanceof运算符 2.3.8 优先级顺序 2.3.9 关于运算符的记忆要点 2.4 条件语句 2.4.1 switch语句 2.4.2 Java中的循环语句 2.4.3 用break、continue和return提前退出循环 2.5 处理运行时异常 2.5.1 使用try和catch块 2.5.2 使用throws子句 2.5.3 关于流程控制语句的记忆要点 2.6 总结 2.7 练习 第3章 带有类的语言:Java面向对象程序设计 3.1 设计一个Java类 3.2 方法的魔法 3.3 关于方法的更多话题 3.3.1 构造函数方法 3.3.2 访问方法 3.3.3 类方法 3.3.4 “其他”方法 3.4 继承 3.5 抽象类 3.6 类修饰符 3.7 接口 3.8 快捷地创建类 3.9 包 3.10 总结 3.11 练习 第4章 Java API为你服务:常用Java类 4.1 java.lang包 4.1.1 java.lang.Object 4.1.2 java.lang.String 4.1.3 java.lang.StringBuffer 4.1.4 封装类 4.1.5 java.lang.Math 4.1.6 java.lang.System 4.1.7 java.lang.Cloneable 4.1.8 java.lang.Thread 4.2 java.io包 4.3 java.util包 4.3.1 java.util.StringTokenizer 4.3.2 java.util.Random 4.3.3 Java2集合框架 4.4 总结 4.5 练习 第2篇 Java 2-D图像开发和抽象Window工具包 第5章 Applet基础 5.1 什么是Java applet 5.2 Applet和Application的比较 5.3 Applet的组成和生命周期 5.4 一个Applet例子 5.5 运行Java Applets 5.6 通用AWT组件 5.6.1 按钮 5.6.2 单选按钮(Radio Button) 5.6.3 作出重要选择 5.6.4 循环播放声音文件 5.6.5 文本域 5.6.6 标签 5.7 布局管理 5.7.1 FlowLayout类 5.7.2 GridLayout类 5.7.3 BorderLayout类 5.7.4 CardLayout类 5.8 容器(Container) 5.9 创建自定义组件 5.10 一个完整的例子 5.11 总结 5.12 练习 第6章 监听用户 6.1 EventLi
JAVA高级特性 1.静态导入:先举个离例子 。 import java.lang.Integer.parseInt; public class StaticImport { int x = parseInt("123"); System.out.println(x); } 这样的程序如果不在IDE 工具中输入,是很难看出这个程序代码会出现问题,可它偏偏就出问题了,这是为什么呢?因为程序里面有个静态方法,如果导入import static java.lang.Integer.parseInt;这样的程序就可以运行了。 2.可变参数与for循环增强 这个是一般的用法: public static void loop(String[] args){ for(int i=0;i<args.length;i++) { System.out.println(args[i]); } } 这个是JDK 增加的新特性的用法! public static void loop(int x,int... args ) { //这里的参数一定要以这样的形式输入 for(int i:args) { System.out.println(i); } } 3.枚举 枚举技巧: 1. enum Gender{MALE,FEMALE,BOTH} 2. enum Gender{MALE,FEMALE,BOTH;public abstract getTitle();} 3.enum Gender{MALE{},FEMALE{},BOTH{};public abstract getTitle();} 4.填充各个{}中的getTitle方法。 下面是个红绿黄灯的例子: public enum TrafficLamp { RED(30){ public TrafficLamp next() { return GREEN; } }, GREEN(50){ public TrafficLamp next() { return YELLOW; } }, YELLOW(5){ public TrafficLamp next() { return RED; } }; public abstract TrafficLamp next(); private int time; private TrafficLamp(int time) { this.time = time; } 4.反射. 这个知识点,真是费了我好大劲才理解。当真正理解了,其实也就不难了。先举例子来理解什么是反射。 先建这样的一个类,带会下面有个类里面有反射成员变量的方法的! public class ReflectPoint { private int x; public int y; public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } } public class Test{ String obj = (String)Class.forName("java.lang.String").newInstance(); 这是制造另一个章:Class.forName("java.lang.Integer") --------------------- 讲Constructor://构造方法 Constructor constructors[] = Class.forName("java.lang.String").getConstructors(); Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class); String str = (String)constructor.newInstance(new StringBuffer("abc")); System.out.println(str); class.newInstance()内部其实就是在用默认的构造方法 ----------------------- 讲Method //方法 Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class); System.out.println(charAt.invoke(str, 1)); ------------------------- 讲Field //成员变量 ReflectPoint point = new ReflectPoint(1,7); Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("y"); y.setAccessible(true); System.out.println(y.get(point)); Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x"); x.setAccessible(true); System.out.println(x.get(point)); } 在这里我们运行的时候可以看出,ReflectPoint类里面的x和y 都可以打印出来了! 学习心得:先从这四个知识点来看,张老师的确很让人敬佩!以上的一些程序代码均为张老师课堂即兴发挥所,也可以看出,张老师对JAVA特性的深刻理解能力!现在说说我对这些程序代码的理解,说实话,才开始听张老师讲的时候,我感觉很模糊的,但是当我真正理解了之后,觉得这些程序的真是太好了。象以后我门在学习JAVA 的时候,一定要对每个知识点要慢慢的消化吃透,切不能走马观花。一个知识点必须要反复的动手练习,不然很难理解其中的奥秘所在的! 5.在JAVA的程序中,我经常性的看到字符前面有@这种标志的符号.这个就叫做注解! 下面是使用 @SuppressWarnings 来取消 deprecation 警告的一个例子: public class Test { @Deprecated //在eclipse下运行的时候,方法名上会加一横线 public static void sayHello() { } } public class Test2 { @SuppressWarnings("deprecation") //在mian方法内调用一个没有定义的方法时,运行的时候将会出现这一注解! public static void main(String [] args) { Test.sayHello(); } } @SuppressWarnings 批注允许您选择性地取消特定代码段(即,类或方法)中的警告。其中的想法是当您看到警告时,您将调查它,如果您确定它不是问题,您就可以添加一个 @SuppressWarnings 批注,以使您不会再看到警告。虽然它听起来似乎会屏蔽潜在的错误,但实际上它将提高代码安全性,因为它将防止您对警告无动于衷 — 您看到的每一个警告都将值得注意。 由如下代码引出@Override的讲解: User类中的方法: public boolean equals(User other) { return name.equals(other.name); } 下面的代码执行时将有问题: User user1 = new User(); User user2 = new User(); user1.setName("abc"); user2.setName("abc"); System.out.println(user1.equals(user2)); HashSet set = new HashSet(); set.add(user1); set.add(user2); System.out.println(set.size());//期望结果为1,但实际为2 这时候在User类的equals方法上加上@Override,发现了问题。 再看看这个代码:一看就知道有问题, 这里 就有个很好的解决办法,在public @interface MyAnnotation {}这样的类的时候,下面的代码上的错误提示就是结束的! public class dsds { public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub System.out.println(User.class.isAnnotationPresent(MyAnnotation.class) ); System.out.println( User.class.getAnnotation(MyAnnotation.class) ); } } 运行的结果为:false null 下面演示了一下@Target和@Retention import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE}) //用于构造方法 @Retention(RetentionPolicy.RUNTIME) //在运行是加载Annotation到JVM中 public @interface MyAnnotation { public String value() default "yellow"; public int[] array() default {1,2}; public MetaAnnotation annotation() ; } 注解最主要的就是这么多,其实注解真正的,我们都很少留心的,但是有的注解会给你在程序和查找错误的时,会有很大的帮助的! 6.泛型: 1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。 2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。 3、泛型的类型参数可以有多个。 4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上成为“有界类型”。 5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(java.lang.String); 例子: a..使用?通配符可以引用其他各种参数化的类型,但不能调用与参数化有关的方法; Collection<?> c = new Vector<String>(); c.add("abc");//报错 c.size();//正确 所以,?通配符定义的变量主要用作引用,调用与参数化无关的方法,如果要调用与参数化相关的方法,那么必须在使用?通配符引用之前调用,否则就与java 5提供泛型的目的背道而驰了。 b..向下限定通配符: 正确:Vector<? extends Number> x = new Vector<Integer>(); 错误:Vector<? extends Number> x = new Vector<String>(); 向上限定通配符: 正确:Vector<? super Integer> x = new Vector<Number>(); 错误:Vector<? super Integer> x = new Vector<Byte>(); 7.代理 这里就直接先应用张老师的代码然后再讲清其原理吧! package cn.itcast.corejava; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Collection; import java.util.Vector; public class ProxyTest { public static void main(String[] args) { System.out.println(Integer.class.getClassLoader()); //System.out.println(ProxyTest.class.getClassLoader().getParent().getClass().getName()); System.out.println(ProxyTest.class.getClassLoader().getClass().getName()); //test1(); //test2(); } private static void test3() { Vector v = new Vector(); class MyInvocationHandler implements InvocationHandler { Collection target = null; public Collection bind(Collection target) { this.target = target; Collection proxy1 = (Collection)Proxy.newProxyInstance( ProxyTest.class.getClassLoader(), new Class[]{Collection.class} , this); return proxy1; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("begin " + method.getName()); Object retval = method.invoke(target, args); System.out.println("end" + method.getName()); return retval; } } MyInvocationHandler handler = new MyInvocationHandler(); Collection proxy1 = handler.bind(v); System.out.println(proxy1.getClass().getName()); proxy1.add("abc"); proxy1.add("xyz"); System.out.println(proxy1.size()); } private static void test2() { Vector v = new Vector(); class MyInvocationHandler implements InvocationHandler { Collection target = null; public MyInvocationHandler(){} public MyInvocationHandler(Collection target){this.target = target;} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("begin " + method.getName()); Object retval = method.invoke(target, args); System.out.println("end" + method.getName()); return retval; } } Collection proxy1 = (Collection)Proxy.newProxyInstance( ProxyTest.class.getClassLoader(), new Class[]{Collection.class} , new MyInvocationHandler(v)); System.out.println(proxy1.getClass().getName()); proxy1.add("abc"); proxy1.add("xyz"); System.out.println(proxy1.size()); } private static void test1() { Collection proxy = (Collection)Proxy.newProxyInstance( ProxyTest.class.getClassLoader(),//first parameter new Class[]{Collection.class} , //second parameter new InvocationHandler(){ //third parameter Vector target = new Vector(); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("begin " + method.getName()); Object retval = method.invoke(target, args); System.out.println("end" + method.getName()); return retval; } } ); System.out.println(proxy.getClass().getName()); proxy.add("abc"); proxy.add("xyz"); System.out.println(proxy.size()); } } 以下是Proxy的API文档: public class Proxyextends extends Object implements Serializable Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. To create a proxy for some interface Foo: InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass( Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass. getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler }); or more simply: Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);从以上的程序中在方法中定义参数的时候总是三个参数,可这三个参数有分别代表什么意思呢!?现在以我自己个人的理解,比如我是买家,我要买本书,可是现在我有事,是不是需要别人代我买呢?那带我买的人是不是一定要到卖书的地方买呢?所以着中间产生了三个实体.说白了就是一种代理机制.需要三方一起运行! 代理其实很好理解的,而且会用固定的语法格式,很快会掌握这一原理的!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值