前言


  • 本文有配套视频,可以酌情观看。
  • 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我。
  • 文中所有内容仅供学习交流之用,不可用于商业用途,如因此引起的相关法律法规责任,与我无关。
  • 如文中内容对您造成不便,烦请联系 277511806@qq.com 处理,谢谢。
  • 转载麻烦注明出处,谢谢。

Android启动页面


  • 从上面的效果可以看出,安卓端还没有启动页面,这边我们就通过React-Native 的方式解决。

    • 思路:新建一个组件作为 Android 的启动页,index.android.js 的初始化窗口改为 Android启动页,设置定时器,使其在1.5秒后自动跳转到 Main 组件。

    export default class GDLaunchPage extends Component { componentDidMount() {
    setTimeout(() => {
    this.props.navigator.replace({
    component:Main
    })
    }, 1500)
    } render() {
    return(
    <Image source={{uri:'launchimage'}} style={styles.imageStyle} />
    );
    }
    }

git使用


  • 项目的版本管理也是程序猿必须具备的一项技能,它能够让我们避免许多开发中遇到的尴尬问题。

  • 公司里面一般使用 SVNGit 两种,而现在 Git 的份额逐渐在蚕食着 SVN,这边我给大家提供了 SVNGit 的详情版,大家可以前往阅读。

  • 这小结建议观看视频,视频内有具体操作!

错误修正 —— 模态


  • 以前看官方文档竟然没有发现 React-Native 提供了 model 组件,在这里给大家道个歉,以后跪着写教程,不用让我起来,反正我感觉膝盖软软的!

  • 前几天在看官方文档的时候,无意中看见 model 组件,我嘞个天,有这东西就可以减少开发中很多功能开发难度。当初怎么没发现,还傻傻地一步一步去封装这个东西 T^T,在这告诫各位,不能太粗心!

  • 这边我们就将原本 近半小时热门 这个模块的跳转模式改成 正宗的 模态,代码如下:

	render() {
return (
<View style={styles.container}>
{/* 初始化模态 */}
<Modal
animationType='slide'
transparent={false}
visible={this.state.isModal}
onRequestClose={() => this.onRequestClose()}
>
<Navigator
initialRoute={{
name:'halfHourHot',
component:HalfHourHot
}} renderScene={(route, navigator) => {
let Component = route.component;
return <Component
removeModal={(data) => this.closeModal(data)}
{...route.params}
navigator={navigator} />
}} />
</Modal> {/* 导航栏样式 */}
<CommunalNavBar
leftItem = {() => this.renderLeftItem()}
titleItem = {() => this.renderTitleItem()}
rightItem = {() => this.renderRightItem()}
/> {/* 根据网络状态决定是否渲染 listview */}
{this.renderListView()}
</View>
);
}

注:这边需要注意一下 逆向传值 的方式,这里用到最基本的逐层传值,类似于 block 的功能,具体的代码参考 Demo , Demo 下载地址在上面。

  • 关于更详细地 model 使用,可以参照官方文档 model ,当然我也给各位上了这道菜 —— React-Native 之 model介绍与使用

  • 通过查看 modal 的源码,我们不难发现 —— 其实 modal 实现原理也只是使用了 绝对定位,所以如果 modal 无法满足我们的功能,我们可以使用 绝对定位 来自己实现一下类似功能。

加载更多功能完善


  • 这边我们来完善一下 加载更多功能数据 的加载,需要注意的一点就是,拼接数组需要使用 concat 方法来拼接,它会返回一个 新的数组 给我们使用,而不修改传入的数组。

  • 这边我们加载数据的方法分为 2 个,代码看起来重复性很高,但是其实这就取决于我们的需求了,我们分为 2 个的好处是看起来更清晰,减少沟通成本,想象一下,如果我们把所有逻辑都放到同一个方法内,那么是不是这个方法内的逻辑是不是特别复杂,不方便后期维护?!所以这就是为什么分为 2 个方法进行加载的原因。

  • 那来看一下加载最新数据这边逻辑:

    	// 加载最新数据网络请求
    loadData(resolve) { let params = {"count" : 10 }; HTTPBase.get('https://guangdiu.com/api/getlist.php', params)
    .then((responseData) => { // 清空数组
    this.data = []; // 拼接数据
    this.data = this.data.concat(responseData.data); // 重新渲染
    this.setState({
    dataSource: this.state.dataSource.cloneWithRows(this.data),
    loaded:true,
    }); // 关闭刷新动画
    if (resolve !== undefined){
    setTimeout(() => {
    resolve();
    }, 1000);
    } // 存储数组中最后一个元素的id
    let cnlastID = responseData.data[responseData.data.length - 1].id;
    AsyncStorage.setItem('cnlastID', cnlastID.toString()); })
    .catch((error) => { })
    }
  • 再来看下加载更多这边的逻辑:

    • 加载更多需要在获取 最新 数据的时候将数组中 最后一个元素 内的ID保存起来,因为不是大批量数据存储,这边我们就使用 AsyncStorage 进行 id 的存储。

    • 接着,我们拼接请求参数。


    // 加载更多数据的网络请求
    loadMoreData(value) { let params = {
    "count" : 10,
    "sinceid" : value
    }; HTTPBase.get('https://guangdiu.com/api/getlist.php', params)
    .then((responseData) => { // 拼接数据
    this.data = this.data.concat(responseData.data); this.setState({
    dataSource: this.state.dataSource.cloneWithRows(this.data),
    loaded:true,
    }); // 存储数组中最后一个元素的id
    let cnlastID = responseData.data[responseData.data.length - 1].id;
    AsyncStorage.setItem('cnlastID', cnlastID.toString());
    })
    .catch((error) => { })
    }

Cell 点击实现


  • 我们回到主页这边来实现以下 cell 的点击,需要注意的是对 row 进行绑定操作,不然会找不到当前的 this

    	// 绑定
    renderRow={this.renderRow.bind(this)}
  • 接着来看下 renderRow 方法实现:

    	// 返回每一行cell的样式
    renderRow(rowData) {
    return(
    <TouchableOpacity
    onPress={() => this.pushToDetail(rowData.id)}
    >
    <CommunalHotCell
    image={rowData.image}
    title={rowData.title}
    />
    </TouchableOpacity>
    );
    }
  • 再来看下 pushToDetail 方法实现,params意思就是将 url 参数传递到 CommunalDetail 组件:

    	// 跳转到详情页
    pushToDetail(value) {
    this.props.navigator.push({
    component:CommunalDetail,
    params: {
    url: 'https://guangdiu.com/api/showdetail.php' + '?' + 'id=' + value
    }
    })
    }

详情页


  • 既然我们已经保存了 id 那么就可以来做详情页了,当我们点击 cell 的时候,需要跳转到对应的 详情页

  • 这边服务器返回给我们的是个 网页数据 ,我们这边就直接使用 webView组件 展示,具体使用我们就不多做介绍了,很简单,详情就参考官方文档 WebView

  • 先来看详情页的实现:

    	export default class GDCommunalDetail extends Component {
    
    	    static propTypes = {
    uri:PropTypes.string,
    }; // 返回
    pop() {
    this.props.navigator.pop();
    } // 返回左边按钮
    renderLeftItem() {
    return(
    <TouchableOpacity
    onPress={() => {this.pop()}}
    >
    <Text>返回</Text>
    </TouchableOpacity>
    );
    } componentWillMount() {
    // 发送通知
    DeviceEventEmitter.emit('isHiddenTabBar', true);
    } componentWillUnmount() {
    // 发送通知
    DeviceEventEmitter.emit('isHiddenTabBar', false);
    } render() {
    return(
    <View style={styles.container}>
    {/* 导航栏 */}
    <CommunalNavBar
    leftItem = {() => this.renderLeftItem()}
    /> {/* 初始化WebView */}
    <WebView
    style={styles.webViewStyle}
    source={{url:this.props.url, method: 'GET' }}
    javaScriptEnabled={true}
    domStorageEnabled={true}
    scalesPageToFit={false}
    />
    </View>
    );
    }
    } const styles = StyleSheet.create({
    container: {
    flex:1
    }, webViewStyle: {
    flex:1
    }
    });
  • 按照上面的方法,我们完成一下 近半小时热门模块 的跳转详情功能。

海淘半小时热门


  • 近半小时热门 效果是一样的,只是请求参数变了,所以 Copy 然后修改下相应参数啊:

    	export default class GDUSHalfHourHot extends Component {
    
    	    // 构造
    constructor(props) {
    super(props);
    // 初始状态
    this.state = {
    dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2}),
    loaded:true,
    };
    this.fetchData = this.fetchData.bind(this);
    } static defaultProps = {
    removeModal:{}
    } // 网络请求
    fetchData(resolve) {
    let params = {
    "c" : "us"
    }; HTTPBase.get('http://guangdiu.com/api/gethots.php', params)
    .then((responseData) => {
    this.setState({
    dataSource: this.state.dataSource.cloneWithRows(responseData.data),
    loaded:true,
    });
    if (resolve !== undefined){
    setTimeout(() => {
    resolve(); // 关闭动画
    }, 1000);
    }
    })
    .catch((error) => { })
    } popToHome(data) {
    this.props.removeModal(data);
    } // 返回中间按钮
    renderTitleItem() {
    return(
    <Text style={styles.navbarTitleItemStyle}>近半小时热门</Text>
    );
    } // 返回右边按钮
    renderRightItem() {
    return(
    <TouchableOpacity
    onPress={()=>{this.popToHome(false)}}
    >
    <Text style={styles.navbarRightItemStyle}>关闭</Text>
    </TouchableOpacity>
    );
    } // 根据网络状态决定是否渲染 listview
    renderListView() {
    if (this.state.loaded === false) {
    return(
    <NoDataView />
    );
    }else {
    return(
    <PullList
    onPullRelease={(resolve) => this.fetchData(resolve)}
    dataSource={this.state.dataSource}
    renderRow={this.renderRow.bind(this)}
    showsHorizontalScrollIndicator={false}
    style={styles.listViewStyle}
    initialListSize={5}
    renderHeader={this.renderHeader}
    />
    );
    }
    } // 返回 listview 头部
    renderHeader() {
    return (
    <View style={styles.headerPromptStyle}>
    <Text>根据每条折扣的点击进行统计,每5分钟更新一次</Text>
    </View>
    );
    } // 跳转到详情页
    pushToDetail(value) {
    this.props.navigator.push({
    component:CommunalDetail,
    params: {
    url: 'https://guangdiu.com/api/showdetail.php' + '?' + 'id=' + value
    }
    })
    } // 返回每一行cell的样式
    renderRow(rowData) {
    return(
    <TouchableOpacity
    onPress={() => this.pushToDetail(rowData.id)}
    >
    <CommunalHotCell
    image={rowData.image}
    title={rowData.title}
    />
    </TouchableOpacity>
    );
    } componentWillMount() {
    // 发送通知
    DeviceEventEmitter.emit('isHiddenTabBar', true);
    } componentWillUnmount() {
    // 发送通知
    DeviceEventEmitter.emit('isHiddenTabBar', false);
    } componentDidMount() {
    this.fetchData();
    } render() {
    return (
    <View style={styles.container}>
    {/* 导航栏样式 */}
    <CommunalNavBar
    titleItem = {() => this.renderTitleItem()}
    rightItem = {() => this.renderRightItem()}
    /> {/* 根据网络状态决定是否渲染 listview */}
    {this.renderListView()}
    </View>
    );
    }
    } const styles = StyleSheet.create({
    container: {
    flex:1,
    alignItems: 'center',
    }, navbarTitleItemStyle: {
    fontSize:17,
    color:'black',
    marginLeft:50
    },
    navbarRightItemStyle: {
    fontSize:17,
    color:'rgba(123,178,114,1.0)',
    marginRight:15
    }, listViewStyle: {
    width:width,
    }, headerPromptStyle: {
    height:44,
    width:width,
    backgroundColor:'rgba(239,239,239,0.5)',
    justifyContent:'center',
    alignItems:'center'
    }
    });

海淘模块


  • 我们可以发现 海淘 这一块和 首页 是类似的,只是数据请求参数不同,所以我们还是 Copy 一下代码,然后将请求参数改为如下:

    	export default class GDHome extends Component {
    
    	    // 构造
    constructor(props) {
    super(props);
    // 初始状态
    this.state = {
    dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2}),
    loaded:false,
    isModal:false
    }; this.data = [];
    this.loadData = this.loadData.bind(this);
    this.loadMore = this.loadMore.bind(this);
    } // 加载最新数据网络请求
    loadData(resolve) { let params = {
    "count" : 10,
    "country" : "us"
    }; HTTPBase.get('https://guangdiu.com/api/getlist.php', params)
    .then((responseData) => { // 拼接数据
    this.data = this.data.concat(responseData.data); // 重新渲染
    this.setState({
    dataSource: this.state.dataSource.cloneWithRows(this.data),
    loaded:true,
    }); // 关闭刷新动画
    if (resolve !== undefined){
    setTimeout(() => {
    resolve();
    }, 1000);
    } // 存储数组中最后一个元素的id
    let uslastID = responseData.data[responseData.data.length - 1].id;
    AsyncStorage.setItem('uslastID', uslastID.toString());
    })
    .catch((error) => { })
    } // 加载更多数据的网络请求
    loadMoreData(value) { let params = {
    "count" : 10,
    "sinceid" : value,
    "country" : "us"
    }; HTTPBase.get('https://guangdiu.com/api/getlist.php', params)
    .then((responseData) => { // 拼接数据
    this.data = this.data.concat(responseData.data); this.setState({
    dataSource: this.state.dataSource.cloneWithRows(this.data),
    loaded:true,
    }); // 存储数组中最后一个元素的id
    let uslastID = responseData.data[responseData.data.length - 1].id;
    AsyncStorage.setItem('uslastID', uslastID.toString()); })
    .catch((error) => { })
    } // 加载更多数据操作
    loadMore() {
    // 读取id
    AsyncStorage.getItem('uslastID')
    .then((value) => {
    // 数据加载操作
    this.loadMoreData(value);
    }) } // 模态到近半小时热门
    pushToHalfHourHot() {
    this.setState({
    isModal:true
    })
    } // 跳转到搜索
    pushToSearch() {
    this.props.navigator.push({
    component:Search,
    })
    } // 安卓模态销毁处理
    onRequestClose() {
    this.setState({
    isModal:false
    })
    } // 关闭模态
    closeModal(data) {
    this.setState({
    isModal:data
    })
    } // 返回左边按钮
    renderLeftItem() {
    return(
    <TouchableOpacity
    onPress={() => {this.pushToHalfHourHot()}}
    >
    <Image source={{uri:'hot_icon_20x20'}} style={styles.navbarLeftItemStyle} />
    </TouchableOpacity>
    );
    } // 返回中间按钮
    renderTitleItem() {
    return(
    <TouchableOpacity>
    <Image source={{uri:'navtitle_home_down_66x20'}} style={styles.navbarTitleItemStyle} />
    </TouchableOpacity>
    );
    } // 返回右边按钮
    renderRightItem() {
    return(
    <TouchableOpacity
    onPress={()=>{this.pushToSearch()}}
    >
    <Image source={{uri:'search_icon_20x20'}} style={styles.navbarRightItemStyle} />
    </TouchableOpacity>
    );
    } // ListView尾部
    renderFooter() {
    return (
    <View style={{height: 100}}>
    <ActivityIndicator />
    </View>
    );
    } // 根据网络状态决定是否渲染 listview
    renderListView() {
    if (this.state.loaded === false) {
    return(
    <NoDataView />
    );
    }else {
    return(
    <PullList
    onPullRelease={(resolve) => this.loadData(resolve)}
    dataSource={this.state.dataSource}
    renderRow={this.renderRow.bind(this)}
    showsHorizontalScrollIndicator={false}
    style={styles.listViewStyle}
    initialListSize={5}
    renderHeader={this.renderHeader}
    onEndReached={this.loadMore}
    onEndReachedThreshold={60}
    renderFooter={this.renderFooter}
    />
    );
    }
    } // 跳转到详情页
    pushToDetail(value) {
    this.props.navigator.push({
    component:CommunalDetail,
    params: {
    url: 'https://guangdiu.com/api/showdetail.php' + '?' + 'id=' + value
    }
    })
    } // 返回每一行cell的样式
    renderRow(rowData) {
    return(
    <TouchableOpacity
    onPress={() => this.pushToDetail(rowData.id)}
    >
    <CommunalHotCell
    image={rowData.image}
    title={rowData.title}
    />
    </TouchableOpacity>
    );
    } componentDidMount() {
    this.loadData();
    } render() {
    return (
    <View style={styles.container}>
    {/* 初始化模态 */}
    <Modal
    animationType='slide'
    transparent={false}
    visible={this.state.isModal}
    onRequestClose={() => this.onRequestClose()}
    >
    <Navigator
    initialRoute={{
    name:'halfHourHot',
    component:USHalfHourHot
    }} renderScene={(route, navigator) => {
    let Component = route.component;
    return <Component
    removeModal={(data) => this.closeModal(data)}
    {...route.params}
    navigator={navigator} />
    }} />
    </Modal> {/* 导航栏样式 */}
    <CommunalNavBar
    leftItem = {() => this.renderLeftItem()}
    titleItem = {() => this.renderTitleItem()}
    rightItem = {() => this.renderRightItem()}
    /> {/* 根据网络状态决定是否渲染 listview */}
    {this.renderListView()}
    </View>
    );
    }
    } const styles = StyleSheet.create({
    container: {
    flex: 1,
    alignItems: 'center',
    backgroundColor: 'white',
    }, navbarLeftItemStyle: {
    width:20,
    height:20,
    marginLeft:15,
    },
    navbarTitleItemStyle: {
    width:66,
    height:20,
    },
    navbarRightItemStyle: {
    width:20,
    height:20,
    marginRight:15,
    }, listViewStyle: {
    width:width,
    },
    });

获取最新数据个数功能


  • 这里需要 cnmaxidusmaxid 参数,他们分别是最新数据中第一个元素的 id,也就是我们每次 刷新 的时候都保存一下数组中的第一个元素的 id

    	 // 首页存储数组中第一个元素的id
    let cnfirstID = responseData.data[0].id;
    AsyncStorage.setItem('cnfirstID', cnfirstID.toString());
  • 这个功能是从程序启动的时候就开始 定时循环执行 ,也就是我们需要放到 入口文件中(Main文件)

    	componentDidMount() {
    // 注册通知
    this.subscription = DeviceEventEmitter.addListener('isHiddenTabBar', (data)=>{this.tongZhi(data)}); // 声明变量
    let cnfirstID = 0;
    let usfirstID = 0; // 最新数据的个数
    setInterval(() => {
    // 取出id
    AsyncStorage.getItem('cnfirstID')
    .then((value) => {
    cnfirstID = parseInt(value);
    });
    AsyncStorage.getItem('usfirstID')
    .then((value) => {
    usfirstID = parseInt(value);
    }); if (cnfirstID !== 0 && usfirstID !== 0) { // 参数不为0
    // 拼接参数
    let params = {
    "cnmaxid" : cnfirstID,
    "usmaxid" : usfirstID
    }; // 请求数据
    HTTPBase.get('http://guangdiu.com/api/getnewitemcount.php', params)
    .then((responseData) => { console.log(responseData);
    this.setState({
    cnbadgeText:responseData.cn,
    usbadgeText:responseData.us
    })
    })
    }
    }, 30000);
    }

注:上面使用到的 setInterval 也是个定时器,和我们之前使用的 setTimeout 不同的是,setInterval 是周期定时器,比如上面时间为 30000毫秒,意思就是每过 30000毫秒 就会执行一次里面的代码。而 setTimeout 则是会在规定的时间后 尽快 执行任务。

React-Native 之 项目实战(三)的更多相关文章

  1. React Native 之 项目实战(一)

    前言 本文有配套视频,可以酌情观看. 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我. 文中所有内容仅供学习交流之用,不可用于商业用途,如因此引起的相关法律法规责任,与我无关. 如文中内容对 ...

  2. React Native商城项目实战04 - 封装TabNavigator.Item的创建

    1.Main.js /** * 主页面 */ import React, { Component } from 'react'; import { StyleSheet, Text, View, Im ...

  3. React Native商城项目实战02 - 主要框架部分(tabBar)

    1.安装插件,cd到项目根目录下执行: $ npm i react-native-tab-navigator --save 2.主框架文件Main.js /** * 主页面 */ import Rea ...

  4. React Native商城项目实战01 - 初始化设置

    1.创建项目 $ react-native init BuyDemo 2.导入图片资源 安卓:把文件夹放到/android/app/src/main/res/目录下,如图: iOS: Xcode打开工 ...

  5. React Native商城项目实战07 - 设置“More”界面导航条

    1.More/More.js /** * 更多 */ import React, { Component } from 'react'; import { AppRegistry, StyleShee ...

  6. React Native商城项目实战05 - 设置首页的导航条

    1.Home.js /** * 首页 */ import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Te ...

  7. React Native商城项目实战06 - 设置安卓中的启动页

    1.Main 目录下新建LaunchImage.js: /** * 启动页 */ import React, { Component } from 'react'; import { AppRegis ...

  8. React Native商城项目实战03 - 包装Navigator

    1.在Home目录下新建首页详细页HomeDetail.js /** * 首页详情页 */ import React, { Component } from 'react'; import { App ...

  9. React Native商城项目实战16 - 购物中心详细页

    逻辑分析: 首页(Home)加载的购物中心组件(ShopCenter),传递url数据: ShopCenter里根据url加载购物中心详细页组件(ShopCenterDetail), ShopCent ...

  10. React Native商城项目实战15 - 首页购物中心

    1.公共的标题栏组件TitleCommonCell.js /** * 首页购物中心 */ import React, { Component } from 'react'; import { AppR ...

随机推荐

  1. React-intl 实现多语言

    前言 React 做国际化,我推荐使用 React-intl , 这个库提供了 React 组件和Api两种方式来格式化日期,数字和字符串等.知道这个库了,那让我们开始使用它 组件用法 为了和Reac ...

  2. 【原创】NuGet 出现“无法初始化 PowerShell 主机,如果将你的 PowerShell 执行策略设置设置为 AllSigned ,请先打开程序包管理控制台以初始化该主机” 错误的解决方法

    现象: 网上的设置 AllSigned 等方法都无效..后来考虑可能跟命令行版本兼容性有关系,然后在注册表命令行配置里发现一 ForceV2 设置项,抱着试一试的心态改了下,果然解决了! 解决方法:修 ...

  3. 通过oledb驱动读取excel、csv数据丢失解决方案

    1.问题出现 在开发应用程序的过程中,比较常用一功能就是通过oledb驱动读取excel.csv.text等文件:而最近有客户反映,在使用短信平台(下载地址:http://www.sms1086.co ...

  4. kafka c++客户端编译

    Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据. 这种动作(网页浏览,搜索和其他用户的行动)是在现代网络上的许多社会功能的一个关键因素. 这些数据通常是 ...

  5. SVN的具体使用方法介绍(安装以及操作)

    今天由于项目的需要安装了SVN,在这里和大家分享一下SVN安装的详细过程和分享一些资料. (1)首先是客户端的安装. 1)获取客户端安装包. --安装包的获取路径: TortoiseSVN的官方下载地 ...

  6. 2017-2-28 C#基础 数组

    1.什么是数组? 数组就是具有相同数据类型变量的集合. 2.数组的作用:操作大量数据. 3.数组的定义要求:(1)数组里面的内容必须是同一类型.(2)数组必须有长度限制. 4.数组分为一维数组,二维数 ...

  7. 每天一个linux命令(39)--ifconfig命令

    许多人非常熟悉Windows下的ipconfig 命令行工具,它被用来获取网络接口配置信息并对此进行修改.Linux系统拥有一个类似的工具,也就是ifconfig(interfaces  config ...

  8. 301、404、200、304、500HTTP状态

    一些常见的状态码为: 200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - 服务器超时 下面提供 HTTP 状态码的完整列表.点击链接可了解详情.您也可以访问 HTTP 状态码上的 ...

  9. C语言在open() FIFO文件的时候卡住了

    需要注意的是,open打开FIFO文件的时候,open函数里的flag参数 O——NONBLOCK: 置位:  如果“只写”方式打开文件,写进程会阻塞直到有一个读进程来读这个FIFO管道.就是说:没有 ...

  10. 《深入理解Java虚拟机》学习笔记之内存回收

    垃圾收集(Garbage Collection,GC)并不是Java语言的半生产物,事实上GC历史远比Java久远,真正使用内存动态分配和垃圾收集技术的语言是诞生于1960年的Lisp语言.经过半个世 ...